using System;
using System.Diagnostics;
using System.Text;
using Community.CsharpSqlite.Entity;
using FILE = System.IO.TextWriter;
using i32 = System.Int32;
#if !SQLITE_MAX_VARIABLE_NUMBER
using ynVar = System.Int16;

#else
using ynVar = System.Int32; 
#endif

namespace Community.CsharpSqlite
{
    using Op = VdbeOp;
    using sqlite3_stmt = Vdbe;
    using sqlite3_value = Mem;

    public class VdbeAux
    {
        /*
    ** 2003 September 6
    **
    ** The author disclaims copyright to this source code.  In place of
    ** a legal notice, here is a blessing:
    **
    **    May you do good and not evil.
    **    May you find forgiveness for yourself and forgive others.
    **    May you share freely, never taking more than you give.
    **
    *************************************************************************
    ** This file contains code used for creating, destroying, and populating
    ** a VDBE (or an "sqlite3_stmt" as it is known to the outside world.)  Prior
    ** to version 2.8.7, all this code was combined into the vdbe.c source file.
    ** But that file was getting too big so this subroutines were split out.
    *************************************************************************
    **  Included in SQLite3 port to C#-SQLite;  2008 Noah B Hart
    **  C#-SQLite is an independent reimplementation of the SQLite software library
    **
    **  SQLITE_SOURCE_ID: 2010-03-09 19:31:43 4ae453ea7be69018d8c16eb8dabe05617397dc4d
    **
    **  $Header: Community.CsharpSqlite/src/vdbeaux_c.cs,v 6604176a7dbe 2010/03/12 23:35:36 Noah $
    *************************************************************************
    */
        //#include "sqliteInt.h"
        //#include "vdbeInt.h"


        /*
    ** When debugging the code generator in a symbolic debugger, one can
    ** set the sqlite3VdbeAddopTrace to 1 and all opcodes will be printed
    ** as they are added to the instruction stream.
    */
#if  SQLITE_DEBUG
        private static bool sqlite3VdbeAddopTrace;
#endif


        /*
** Create a new virtual database engine.
*/

        public static Vdbe VdbeCreate(sqlite3 db)
        {
            Vdbe p;
            p = new Vdbe(); // Malloc.DbMallocZero(db, Vdbe).Length;
            if (p == null) return null;
            p.db = db;
            if (db.pVdbe != null)
            {
                db.pVdbe.pPrev = p;
            }
            p.pNext = db.pVdbe;
            p.pPrev = null;
            db.pVdbe = p;
            p.magic = VdbeMagic.VDBE_MAGIC_INIT;
            return p;
        }

        /*
    ** Remember the SQL string for a prepared statement.
    */

        public static void VdbeSetSql(Vdbe p, string z, int n, int isPrepareV2)
        {
            Debug.Assert(isPrepareV2 == 1 || isPrepareV2 == 0);
            if (p == null) return;
#if SQLITE_OMIT_TRACE
if( 0==isPrepareV2 ) return;
#endif
            Debug.Assert(p.zSql == "");
            p.zSql = z.Substring(0, n); // sqlite3DbStrNDup(p.db, z, n);
            p.isPrepareV2 = isPrepareV2 != 0;
        }

        /*
    ** Return the SQL associated with a prepared statement
    */

        public static string sqlite3_sql(sqlite3_stmt pStmt)
        {
            Vdbe p = pStmt;
            return (p != null && p.isPrepareV2 ? p.zSql : "");
        }

        /*
    ** Swap all content between two VDBE structures.
    */

        public static void VdbeSwap(Vdbe pA, Vdbe pB)
        {
            var tmp = new Vdbe();
            var pTmp = new Vdbe();
            string zTmp;
            pA.CopyTo(tmp);
            pB.CopyTo(pA);
            tmp.CopyTo(pB);
            pTmp = pA.pNext;
            pA.pNext = pB.pNext;
            pB.pNext = pTmp;
            pTmp = pA.pPrev;
            pA.pPrev = pB.pPrev;
            pB.pPrev = pTmp;
            zTmp = pA.zSql;
            pA.zSql = pB.zSql;
            pB.zSql = zTmp;
            pB.isPrepareV2 = pA.isPrepareV2;
        }

#if  SQLITE_DEBUG
        /*
** Turn tracing on or off
*/

        public static void VdbeTrace(Vdbe p, FILE trace)
        {
            p.trace = trace;
        }
#endif

        /*
** Resize the Vdbe.aOp array so that it is at least one op larger than
** it was.
**
** If an out-of-memory error occurs while resizing the array, return
** StatusCode.SQLITE_NOMEM. In this case Vdbe.aOp and Vdbe.nOpAlloc remain
** unchanged (this is so that any opcodes already allocated can be
** correctly deallocated along with the rest of the Vdbe).
*/

        private static int growOpArray(Vdbe p)
        {
            //VdbeOp pNew;
            int nNew = (p.nOpAlloc != 0 ? p.nOpAlloc * 2 : 1024 / 4); //(int)(1024/sizeof(Op)));
            // pNew = Malloc.DbRealloc( p.db, p.aOp, nNew * sizeof( Op ) );
            //if (pNew != null)
            //{
            //      p.nOpAlloc = sqlite3DbMallocSize(p.db, pNew)/sizeof(Op);
            //  p.aOp = pNew;
            //}
            p.nOpAlloc = nNew;
            if (p.aOp == null) p.aOp = new VdbeOp[nNew];
            else Array.Resize(ref p.aOp, nNew);
            return (p.aOp != null ? StatusCode.SQLITE_OK : StatusCode.SQLITE_NOMEM); //  return (pNew ? StatusCode.SQLITE_OK : StatusCode.SQLITE_NOMEM);
        }

        /*
    ** Add a new instruction to the list of instructions current in the
    ** VDBE.  Return the address of the new instruction.
    **
    ** Parameters:
    **
    **    p               Pointer to the VDBE
    **
    **    op              The opcode for this instruction
    **
    **    p1, p2, p3      Operands
    **
    ** Use the VdbeAux.VdbeResolveLabel() function to fix an address and
    ** the VdbeChangeP4() function to change the value of the P4
    ** operand.
    */

        public static int VdbeAddOp3(Vdbe p, int op, int p1, int p2, int p3)
        {
            int i;
            VdbeOp pOp;

            i = p.nOp;
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_INIT);
            Debug.Assert(op > 0 && op < 0xff);
            if (p.nOpAlloc <= i)
            {
                if (growOpArray(p) != 0)
                {
                    return 1;
                }
            }
            p.nOp++;
            if (p.aOp[i] == null) p.aOp[i] = new VdbeOp();
            pOp = p.aOp[i];
            pOp.opcode = (byte)op;
            pOp.p5 = 0;
            pOp.p1 = p1;
            pOp.p2 = p2;
            pOp.p3 = p3;
            pOp.p4.p = null;
            pOp.p4type = P4Type.P4_NOTUSED;
            p.expired = false;
            //VdbeAux.VdbePrintOp(null, i, p.aOp[i]);
#if  SQLITE_DEBUG
            pOp.zComment = null;
            if (sqlite3VdbeAddopTrace) VdbePrintOp(null, i, p.aOp[i]);
#endif
#if VDBE_PROFILE
pOp.cycles = 0;
pOp.cnt = 0;
#endif
            return i;
        }

        public static int VdbeAddOp0(Vdbe p, int op)
        {
            return VdbeAddOp3(p, op, 0, 0, 0);
        }

        public static int VdbeAddOp1(Vdbe p, int op, int p1)
        {
            return VdbeAddOp3(p, op, p1, 0, 0);
        }

        public static int VdbeAddOp2(Vdbe p, int op, int p1, bool b2)
        {
            return VdbeAddOp2(p, op, p1, (b2 ? 1 : 0));
        }

        public static int VdbeAddOp2(Vdbe p, int op, int p1, int p2)
        {
            return VdbeAddOp3(p, op, p1, p2, 0);
        }


        /*
    ** Add an opcode that includes the p4 value as a pointer.
    */
        //P4Type.P4_INT32
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, i32 pP4, int p4type)
        {
            var _p4 = new union_p4();
            _p4.i = pP4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //char
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, char pP4, int p4type)
        {
            var _p4 = new union_p4();
            _p4.z = pP4.ToString();
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //String
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, string pP4, int p4type)
        {
            //      Debug.Assert( pP4 != null );
            var _p4 = new union_p4();
            _p4.z = pP4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, byte[] pP4, int p4type)
        {
            Debug.Assert(op == OPCode.OP_Null || pP4 != null);
            var _p4 = new union_p4();
            _p4.z = Encoding.UTF8.GetString(pP4, 0, pP4.Length);
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //P4Type.P4_INTARRAY
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, int[] pP4, int p4type)
        {
            Debug.Assert(pP4 != null);
            var _p4 = new union_p4();
            _p4.ai = pP4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //P4Type.P4_INT64
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, long pP4, int p4type)
        {
            var _p4 = new union_p4();
            _p4.pI64 = pP4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //DOUBLE (REAL)
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, double pP4, int p4type)
        {
            var _p4 = new union_p4();
            _p4.pReal = pP4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //FUNCDEF
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, FuncDef pP4, int p4type)
        {
            var _p4 = new union_p4();
            _p4.pFunc = pP4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //CollSeq
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, CollSeq pP4, int p4type)
        {
            var _p4 = new union_p4();
            _p4.pColl = pP4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //KeyInfo
        public static int VdbeAddOp4(Vdbe p, int op, int p1, int p2, int p3, KeyInfo pP4, int p4type)
        {
            var _p4 = new union_p4();
            _p4.pKeyInfo = pP4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, p4type);
            return addr;
        }

        //  static int VdbeAddOp4(
        //  Vdbe p,               /* Add the opcode to this VM */
        //  int op,               /* The new opcode */
        //  int p1,               /* The P1 operand */
        //  int p2,               /* The P2 operand */
        //  int p3,               /* The P3 operand */
        //  union_p4 _p4,         /* The P4 operand */
        //  int p4type            /* P4 operand type */
        //)
        //  {
        //    int addr = VdbeAux.VdbeAddOp3(p, op, p1, p2, p3);
        //    VdbeChangeP4(p, addr, _p4, p4type);
        //    return addr;
        //  }

        /*
    ** Add an opcode that includes the p4 value as an integer.
    */

        public static int VdbeAddOp4Int(
            Vdbe p, /* Add the opcode to this VM */
            int op, /* The new opcode */
            int p1, /* The P1 operand */
            int p2, /* The P2 operand */
            int p3, /* The P3 operand */
            int p4 /* The P4 operand as an integer */
            )
        {
            var _p4 = new union_p4();
            _p4.i = p4;
            int addr = VdbeAddOp3(p, op, p1, p2, p3);
            VdbeChangeP4(p, addr, _p4, P4Type.P4_INT32);
            return addr;
        }

        /*
    ** Create a new symbolic label for an instruction that has yet to be
    ** coded.  The symbolic label is really just a negative number.  The
    ** label can be used as the P2 value of an operation.  Later, when
    ** the label is resolved to a specific address, the VDBE will scan
    ** through its operation list and change all values of P2 which match
    ** the label into the resolved address.
    **
    ** The VDBE knows that a P2 value is a label because labels are
    ** always negative and P2 values are suppose to be non-negative.
    ** Hence, a negative P2 value is a label that has yet to be resolved.
    **
    ** Zero is returned if a malloc() fails.
    */

        public static int VdbeMakeLabel(Vdbe p)
        {
            int i;
            i = p.nLabel++;
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_INIT);
            if (i >= p.nLabelAlloc)
            {
                int n = p.nLabelAlloc == 0 ? 15 : p.nLabelAlloc * 2 + 5;
                if (p.aLabel == null) p.aLabel = Malloc.sqlite3Malloc(p.aLabel, n);
                else Array.Resize(ref p.aLabel, n);
                //p.aLabel = Malloc.DbReallocOrFree(p.db, p.aLabel,
                //                                       n*sizeof(p.aLabel[0]));
                p.nLabelAlloc = p.aLabel.Length; //sqlite3DbMallocSize(p.db, p.aLabel)/sizeof(p.aLabel[0]);
            }
            if (p.aLabel != null)
            {
                p.aLabel[i] = -1;
            }
            return -1 - i;
        }

        /*
    ** Resolve label "x" to be the address of the next instruction to
    ** be inserted.  The parameter "x" must have been obtained from
    ** a prior call to VdbeAux.VdbeMakeLabel().
    */

        public static void VdbeResolveLabel(Vdbe p, int x)
        {
            int j = -1 - x;
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_INIT);
            Debug.Assert(j >= 0 && j < p.nLabel);
            if (p.aLabel != null)
            {
                p.aLabel[j] = p.nOp;
            }
        }

        /*
    ** Mark the VDBE as one that can only be run one time.
    */

        public static void VdbeRunOnlyOnce(Vdbe p)
        {
            p.runOnlyOnce = 1;
        }

#if SQLITE_DEBUG
        //* sqlite3AssertMayAbort() logic */


        private static Op opIterNext(VdbeOpIter p)
        {
            Vdbe v = p.v;
            Op pRet = null;
            Op[] aOp;
            int nOp;

            if (p.iSub <= p.nSub)
            {
                if (p.iSub == 0)
                {
                    aOp = v.aOp;
                    nOp = v.nOp;
                }
                else
                {
                    aOp = p.apSub[p.iSub - 1].aOp;
                    nOp = p.apSub[p.iSub - 1].nOp;
                }
                Debug.Assert(p.iAddr < nOp);

                pRet = aOp[p.iAddr];
                p.iAddr++;
                if (p.iAddr == nOp)
                {
                    p.iSub++;
                    p.iAddr = 0;
                }

                if (pRet.p4type == P4Type.P4_SUBPROGRAM)
                {
                    //int nByte =  p.nSub + 1 ) * sizeof( SubProgram* );
                    int j;
                    for (j = 0; j < p.nSub; j++)
                    {
                        if (p.apSub[j] == pRet.p4.pProgram) break;
                    }
                    if (j == p.nSub)
                    {
                        Array.Resize(ref p.apSub, p.nSub + 1); /// Malloc.DbReallocOrFree( v.db, p.apSub, nByte );
                        //if( null==p.apSub ){
                        //  pRet = null;
                        //}else{
                        p.apSub[p.nSub++] = pRet.p4.pProgram;
                        //}
                    }
                }
            }

            return pRet;
        }

        /*
    ** Check if the program stored in the VM associated with pParse may
    ** throw an ABORT exception (causing the statement, but not entire transaction
    ** to be rolled back). This condition is true if the main program or any
    ** sub-programs contains any of the following:
    **
    **   *  OPCode.OP_Halt with P1=StatusCode.SQLITE_CONSTRAINT and P2=OnConstraintError.OE_Abort.
    **   *  OPCode.OP_HaltIfNull with P1=StatusCode.SQLITE_CONSTRAINT and P2=OnConstraintError.OE_Abort.
    **   *  OPCode.OP_Destroy
    **   *  OPCode.OP_VUpdate
    **   *  OPCode.OP_VRename
    **   *  OPCode.OP_FkCounter with P2==0 (immediate foreign key constraint)
    **
    ** Then check that the value of Parse.mayAbort is true if an
    ** ABORT may be thrown, or false otherwise. Return true if it does
    ** match, or false otherwise. This function is intended to be used as
    ** part of an assert statement in the compiler. Similar to:
    **
    **   assert( VdbeAux.VdbeAssertMayAbort(pParse->pVdbe, pParse->mayAbort) );
    */

        public static int VdbeAssertMayAbort(Vdbe v, int mayAbort)
        {
            int hasAbort = 0;
            Op pOp;
            VdbeOpIter sIter;
            sIter = new VdbeOpIter(); // memset( &sIter, 0, sizeof( sIter ) );
            sIter.v = v;

            while ((pOp = opIterNext(sIter)) != null)
            {
                int opcode = pOp.opcode;
                if (opcode == OPCode.OP_Destroy || opcode == OPCode.OP_VUpdate || opcode == OPCode.OP_VRename
#if !SQLITE_OMIT_FOREIGN_KEY
 || (opcode == OPCode.OP_FkCounter && pOp.p1 == 0 && pOp.p2 == 1)
#endif
 || ((opcode == OPCode.OP_Halt || opcode == OPCode.OP_HaltIfNull)
                        && (pOp.p1 == StatusCode.SQLITE_CONSTRAINT && pOp.p2 == OnConstraintError.OE_Abort))
                    )
                {
                    hasAbort = 1;
                    break;
                }
            }
            sIter.apSub = null; // MemPool.DbFree( v.db, sIter.apSub );

            /* Return true if hasAbort==mayAbort. Or if a malloc failure occured.
      ** If malloc failed, then the while() loop above may not have iterated
      ** through all opcodes and hasAbort may be set incorrectly. Return
      ** true for this case to prevent the assert() in the callers frame
      ** from failing.  */
            return (hasAbort == mayAbort) ? 1 : 0; //v.db.mallocFailed !=0|| hasAbort==mayAbort );
        }
#endif
        //* SQLITE_DEBUG - the sqlite3AssertMayAbort() function */

        /*
** Loop through the program looking for P2 values that are negative
** on jump instructions.  Each such value is a label.  Resolve the
** label by setting the P2 value to its correct non-zero value.
**
** This routine is called once after all opcodes have been inserted.
**
** Variable *pMaxFuncArgs is set to the maximum value of any P2 argument 
** to an OPCode.OP_Function, OPCode.OP_AggStep or OPCode.OP_VFilter opcode. This is used by 
** VdbeAux.VdbeMakeReady() to size the Vdbe.apArg[] array.
**
** The Op.opflags field is set on all opcodes.
*/

        private static void resolveP2Values(Vdbe p, ref int pMaxFuncArgs)
        {
            int i;
            int nMaxArgs = pMaxFuncArgs;
            Op pOp;
            int[] aLabel = p.aLabel;
            p.readOnly = true;
            for (i = 0; i < p.nOp; i++) //  for(pOp=p->aOp, i=p->nOp-1; i>=0; i--, pOp++)
            {
                pOp = p.aOp[i];
                byte opcode = pOp.opcode;

                pOp.opflags = (byte)Global.Sqlite3OpcodeProperty[opcode];
                if (opcode == OPCode.OP_Function || opcode == OPCode.OP_AggStep)
                {
                    if (pOp.p5 > nMaxArgs) nMaxArgs = pOp.p5;
                }
                else if (opcode == OPCode.OP_Transaction && pOp.p2 != 0)
                {
                    p.readOnly = false;
#if ! SQLITE_OMIT_VIRTUALTABLE
    }else if( opcode==OPCode.OP_VUpdate ){
      if( pOp->p2>nMaxArgs ) nMaxArgs = pOp->p2;
    }else if( opcode==OPCode.OP_VFilter ){
int n;
Debug.Assert( i < p.nOp - 3 );
Debug.Assert( pOp[-1].opcode==OPCode.OP_Integer );
n = pOp[-1].p1;
if( n>nMaxArgs ) nMaxArgs = n;
#endif
                }

                if ((pOp.opflags & OPFlag.OPFLG_JUMP) != 0 && pOp.p2 < 0)
                {
                    Debug.Assert(-1 - pOp.p2 < p.nLabel);
                    pOp.p2 = aLabel[-1 - pOp.p2];
                }
            }
            MemPool.DbFree(p.db, ref p.aLabel);

            pMaxFuncArgs = nMaxArgs;
        }

        /*
    ** Return the address of the next instruction to be inserted.
    */

        public static int VdbeCurrentAddr(Vdbe p)
        {
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_INIT);
            return p.nOp;
        }

        /*
    ** This function returns a pointer to the array of opcodes associated with
    ** the Vdbe passed as the first argument. It is the callers responsibility
    ** to arrange for the returned array to be eventually freed using the 
    ** vdbeFreeOpArray() function.
    **
    ** Before returning, *pnOp is set to the number of entries in the returned
    ** array. Also, *pnMaxArg is set to the larger of its current value and 
    ** the number of entries in the Vdbe.apArg[] array required to execute the 
    ** returned program.
    */

        public static VdbeOp[] VdbeTakeOpArray(Vdbe p, ref int pnOp, ref int pnMaxArg)
        {
            VdbeOp[] aOp = p.aOp;
            Debug.Assert(aOp != null); // && 0==p.db.mallocFailed );

            /* Check that VdbeAux.VdbeUsesBtree() was not called on this VM */
            Debug.Assert(p.aMutex == null || p.aMutex.nMutex == 0);

            resolveP2Values(p, ref pnMaxArg);
            pnOp = p.nOp;
            p.aOp = null;
            return aOp;
        }

        /*
    ** Add a whole list of operations to the operation stack.  Return the
    ** address of the first operation added.
    */

        public static int VdbeAddOpList(Vdbe p, int nOp, VdbeOpList[] aOp)
        {
            int addr;
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_INIT);
            if (p.nOp + nOp > p.nOpAlloc && growOpArray(p) != 0)
            {
                return 0;
            }
            addr = p.nOp;
            if (UnitTest.ALWAYS(nOp > 0))
            {
                int i;
                VdbeOpList pIn;
                for (i = 0; i < nOp; i++)
                {
                    pIn = aOp[i];
                    int p2 = pIn.p2;
                    if (p.aOp[i + addr] == null) p.aOp[i + addr] = new VdbeOp();
                    VdbeOp pOut = p.aOp[i + addr];
                    pOut.opcode = pIn.opcode;
                    pOut.p1 = pIn.p1;
                    if (p2 < 0 && (Global.Sqlite3OpcodeProperty[pOut.opcode] & OPFlag.OPFLG_JUMP) != 0)
                    {
                        pOut.p2 = addr + (-1 - p2); // Utility.Addr(p2);
                    }
                    else
                    {
                        pOut.p2 = p2;
                    }
                    pOut.p3 = pIn.p3;
                    pOut.p4type = P4Type.P4_NOTUSED;
                    pOut.p4.p = null;
                    pOut.p5 = 0;
#if  SQLITE_DEBUG
                    pOut.zComment = null;
                    if (sqlite3VdbeAddopTrace)
                    {
                        VdbePrintOp(null, i + addr, p.aOp[i + addr]);
                    }
#endif
                }
                p.nOp += nOp;
            }
            return addr;
        }

        /*
    ** Change the value of the P1 operand for a specific instruction.
    ** This routine is useful when a large program is loaded from a
    ** static array using VdbeAux.VdbeAddOpList but we want to make a
    ** few minor changes to the program.
    */

        public static void VdbeChangeP1(Vdbe p, int addr, int val)
        {
            Debug.Assert(p != null);
            Debug.Assert(addr >= 0);
            if (p.nOp > addr)
            {
                p.aOp[addr].p1 = val;
            }
        }

        /*
    ** Change the value of the P2 operand for a specific instruction.
    ** This routine is useful for setting a jump destination.
    */

        public static void VdbeChangeP2(Vdbe p, int addr, int val)
        {
            Debug.Assert(p != null);
            Debug.Assert(addr >= 0);
            if (p.nOp > addr)
            {
                p.aOp[addr].p2 = val;
            }
        }

        /*
    ** Change the value of the P3 operand for a specific instruction.
    */

        public static void VdbeChangeP3(Vdbe p, int addr, int val)
        {
            Debug.Assert(p != null);
            Debug.Assert(addr >= 0);
            if (p.nOp > addr)
            {
                p.aOp[addr].p3 = val;
            }
        }

        /*
    ** Change the value of the P5 operand for the most recently
    ** added operation.
    */

        public static void VdbeChangeP5(Vdbe p, byte val)
        {
            Debug.Assert(p != null);
            if (p.aOp != null)
            {
                Debug.Assert(p.nOp > 0);
                p.aOp[p.nOp - 1].p5 = val;
            }
        }

        /*
    ** Change the P2 operand of instruction addr so that it points to
    ** the address of the next instruction to be coded.
    */

        public static void VdbeJumpHere(Vdbe p, int addr)
        {
            VdbeChangeP2(p, addr, p.nOp);
        }


        /*
    ** If the input FuncDef structure is ephemeral, then free it.  If
    ** the FuncDef is not ephermal, then do nothing.
    */

        private static void freeEphemeralFunction(sqlite3 db, FuncDef pDef)
        {
            if (UnitTest.ALWAYS(pDef) && (pDef.flags & FuncDefFlag.SQLITE_FUNC_EPHEM) != 0)
            {
                pDef = null;
                MemPool.DbFree(db, ref pDef);
            }
        }

        /*
    ** Delete a P4 value if necessary.
    */

        private static void freeP4(sqlite3 db, int p4type, object p4)
        {
            if (p4 != null)
            {
                switch (p4type)
                {
                    case P4Type.P4_REAL:
                    case P4Type.P4_INT64:
                    case P4Type.P4_MPRINTF:
                    case P4Type.P4_DYNAMIC:
                    case P4Type.P4_KEYINFO:
                    case P4Type.P4_INTARRAY:
                    case P4Type.P4_KEYINFO_HANDOFF:
                        {
                            MemPool.DbFree(db, ref p4);
                            break;
                        }
                    case P4Type.P4_VDBEFUNC:
                        {
                            var pVdbeFunc = (VdbeFunc)p4;
                            freeEphemeralFunction(db, pVdbeFunc.pFunc);
                            VdbeDeleteAuxData(pVdbeFunc, 0);
                            MemPool.DbFree(db, ref pVdbeFunc);
                            break;
                        }
                    case P4Type.P4_FUNCDEF:
                        {
                            freeEphemeralFunction(db, (FuncDef)p4);
                            break;
                        }
                    case P4Type.P4_MEM:
                        {
                            p4 = null; // sqlite3ValueFree(ref (sqlite3_value)p4);
                            break;
                        }
                    case P4Type.P4_VTAB:
                        {
                            sqlite3VtabUnlock((VTable)p4);
                            break;
                        }
                    case P4Type.P4_SUBPROGRAM:
                        {
                            VdbeProgramDelete(db, (SubProgram)p4, 1);
                            break;
                        }
                }
            }
        }

        /*
    ** Free the space allocated for aOp and any p4 values allocated for the
    ** opcodes contained within. If aOp is not NULL it is assumed to contain 
    ** nOp entries. 
    */

        private static void vdbeFreeOpArray(sqlite3 db, ref Op[] aOp, int nOp)
        {
            if (aOp != null)
            {
                //Op pOp;
                //    for(pOp=aOp; pOp<&aOp[nOp]; pOp++){
                //      freeP4(db, pOp.p4type, pOp.p4.p);
                //#if SQLITE_DEBUG
                //      MemPool.DbFree(db, ref pOp.zComment);
                //#endif     
                //    }
                //  }
                //  MemPool.DbFree(db, aOp);
                aOp = null;
            }
        }

        /*
    ** Decrement the ref-count on the SubProgram structure passed as the
    ** second argument. If the ref-count reaches zero, free the structure.
    **
    ** The array of VDBE opcodes stored as SubProgram.aOp is freed if
    ** either the ref-count reaches zero or parameter freeop is non-zero.
    **
    ** Since the array of opcodes pointed to by SubProgram.aOp may directly
    ** or indirectly contain a reference to the SubProgram structure itself.
    ** By passing a non-zero freeop parameter, the caller may ensure that all
    ** SubProgram structures and their aOp arrays are freed, even when there
    ** are such circular references.
    */

        public static void VdbeProgramDelete(sqlite3 db, SubProgram p, int freeop)
        {
            if (p != null)
            {
                Debug.Assert(p.nRef > 0);
                if (freeop != 0 || p.nRef == 1)
                {
                    Op[] aOp = p.aOp;
                    p.aOp = null;
                    vdbeFreeOpArray(db, ref aOp, p.nOp);
                    p.nOp = 0;
                }
                p.nRef--;
                if (p.nRef == 0)
                {
                    p = null;
                    MemPool.DbFree(db, ref p);
                }
            }
        }

        /*
    ** Change N opcodes starting at addr to No-ops.
    */

        public static void VdbeChangeToNoop(Vdbe p, int addr, int N)
        {
            if (p.aOp != null)
            {
                sqlite3 db = p.db;
                while (N-- > 0)
                {
                    VdbeOp pOp = p.aOp[addr + N];
                    freeP4(db, pOp.p4type, pOp.p4.p);
                    pOp = p.aOp[addr + N] = new VdbeOp(); //memset(pOp, 0, sizeof(pOp[0]));
                    pOp.opcode = OPCode.OP_Noop;
                    //pOp++;
                }
            }
        }

        /*
    ** Change the value of the P4 operand for a specific instruction.
    ** This routine is useful when a large program is loaded from a
    ** static array using VdbeAux.VdbeAddOpList but we want to make a
    ** few minor changes to the program.
    **
    ** If n>=0 then the P4 operand is dynamic, meaning that a copy of
    ** the string is made into memory obtained from Malloc.sqlite3Malloc().
    ** A value of n==0 means copy bytes of zP4 up to and including the
    ** first null byte.  If n>0 then copy n+1 bytes of zP4.
    **
    ** If n==P4Type.P4_KEYINFO it means that zP4 is a pointer to a KeyInfo structure.
    ** A copy is made of the KeyInfo structure into memory obtained from
    ** Malloc.sqlite3Malloc, to be freed when the Vdbe is finalized.
    ** n==P4Type.P4_KEYINFO_HANDOFF indicates that zP4 points to a KeyInfo structure
    ** stored in memory that the caller has obtained from Malloc.sqlite3Malloc. The
    ** caller should not free the allocation, it will be freed when the Vdbe is
    ** finalized.
    **
    ** Other values of n (P4Type.P4_STATIC, P4Type.P4_COLLSEQ etc.) indicate that zP4 points
    ** to a string or structure that is guaranteed to exist for the lifetime of
    ** the Vdbe. In these cases we can just copy the pointer.
    **
    ** If addr<0 then change P4 on the most recently inserted instruction.
    */

        //P4Type.P4_COLLSEQ
        public static void VdbeChangeP4(Vdbe p, int addr, CollSeq pColl, int n)
        {
            var _p4 = new union_p4();
            _p4.pColl = pColl;
            VdbeChangeP4(p, addr, _p4, n);
        }

        //P4Type.P4_FUNCDEF
        public static void VdbeChangeP4(Vdbe p, int addr, FuncDef pFunc, int n)
        {
            var _p4 = new union_p4();
            _p4.pFunc = pFunc;
            VdbeChangeP4(p, addr, _p4, n);
        }

        //P4Type.P4_INT32
        public static void VdbeChangeP4(Vdbe p, int addr, int i32n, int n)
        {
            var _p4 = new union_p4();
            _p4.i = i32n;
            VdbeChangeP4(p, addr, _p4, n);
        }

        //P4Type.P4_KEYINFO
        public static void VdbeChangeP4(Vdbe p, int addr, KeyInfo pKeyInfo, int n)
        {
            var _p4 = new union_p4();
            _p4.pKeyInfo = pKeyInfo;
            VdbeChangeP4(p, addr, _p4, n);
        }

        //CHAR
        public static void VdbeChangeP4(Vdbe p, int addr, char c, int n)
        {
            var _p4 = new union_p4();
            _p4.z = c.ToString();
            VdbeChangeP4(p, addr, _p4, n);
        }

        //MEM
        public static void VdbeChangeP4(Vdbe p, int addr, Mem m, int n)
        {
            var _p4 = new union_p4();
            _p4.pMem = m;
            VdbeChangeP4(p, addr, _p4, n);
        }

        //STRING

        //STRING + Type
        public static void VdbeChangeP4(Vdbe p, int addr, string z, dxDel P4_Type)
        {
            var _p4 = new union_p4();
            _p4.z = z;
            VdbeChangeP4(p, addr, _p4, P4Type.P4_DYNAMIC);
        }

        //SUBPROGRAM
        public static void VdbeChangeP4(Vdbe p, int addr, SubProgram pProgram, int n)
        {
            var _p4 = new union_p4();
            _p4.pProgram = pProgram;
            VdbeChangeP4(p, addr, _p4, n);
        }

        public static void VdbeChangeP4(Vdbe p, int addr, string z, int n)
        {
            var _p4 = new union_p4();
            if (n > 0 && n <= z.Length) _p4.z = z.Substring(0, n);
            else _p4.z = z;
            VdbeChangeP4(p, addr, _p4, n);
        }

        public static void VdbeChangeP4(Vdbe p, int addr, union_p4 _p4, int n)
        {
            Op pOp;
            sqlite3 db;
            Debug.Assert(p != null);
            db = p.db;
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_INIT);
            if (p.aOp == null /*|| db.mallocFailed != 0 */)
            {
                if (n != P4Type.P4_KEYINFO && n != P4Type.P4_VTAB)
                {
                    freeP4(db, n, _p4);
                }
                return;
            }
            Debug.Assert(p.nOp > 0);
            Debug.Assert(addr < p.nOp);
            if (addr < 0)
            {
                addr = p.nOp - 1;
            }
            pOp = p.aOp[addr];
            freeP4(db, pOp.p4type, pOp.p4.p);
            pOp.p4.p = null;
            if (n == P4Type.P4_INT32)
            {
                /* Note: this cast is safe, because the origin data point was an int
        ** that was cast to a (const char *). */
                pOp.p4.i = _p4.i; // SQLITE_PTR_TO_INT(zP4);
                pOp.p4type = P4Type.P4_INT32;
            }
            else if (n == P4Type.P4_INT64)
            {
                pOp.p4.pI64 = _p4.pI64;
                pOp.p4type = n;
            }
            else if (n == P4Type.P4_REAL)
            {
                pOp.p4.pReal = _p4.pReal;
                pOp.p4type = n;
            }
            else if (_p4 == null)
            {
                pOp.p4.p = null;
                pOp.p4type = P4Type.P4_NOTUSED;
            }
            else if (n == P4Type.P4_KEYINFO)
            {
                KeyInfo pKeyInfo;
                int nField, nByte;

                nField = _p4.pKeyInfo.nField;
                //nByte = sizeof(*pKeyInfo) + (nField-1)*sizeof(pKeyInfo.aColl[0]) + nField;
                pKeyInfo = new KeyInfo(); //Malloc.sqlite3Malloc( nByte );
                pOp.p4.pKeyInfo = pKeyInfo;
                if (pKeyInfo != null)
                {
                    //byte *aSortOrder;
                    //memcpy(pKeyInfo, zP4, nByte);
                    //aSortOrder = pKeyInfo.aSortOrder;
                    //if( aSortOrder ){
                    //  pKeyInfo.aSortOrder = (unsigned char*)&pKeyInfo.aColl[nField];
                    //  memcpy(pKeyInfo.aSortOrder, aSortOrder, nField);
                    //}
                    pKeyInfo = _p4.pKeyInfo.Copy();
                    pOp.p4type = P4Type.P4_KEYINFO;
                }
                else
                {
                    //p.db.mallocFailed = 1;
                    pOp.p4type = P4Type.P4_NOTUSED;
                }
                pOp.p4.pKeyInfo = _p4.pKeyInfo;
                pOp.p4type = P4Type.P4_KEYINFO;
            }
            else if (n == P4Type.P4_KEYINFO_HANDOFF || n == P4Type.P4_KEYINFO_STATIC)
            {
                pOp.p4.pKeyInfo = _p4.pKeyInfo;
                pOp.p4type = P4Type.P4_KEYINFO;
            }
            else if (n == P4Type.P4_FUNCDEF)
            {
                pOp.p4.pFunc = _p4.pFunc;
                pOp.p4type = P4Type.P4_FUNCDEF;
            }
            else if (n == P4Type.P4_COLLSEQ)
            {
                pOp.p4.pColl = _p4.pColl;
                pOp.p4type = P4Type.P4_COLLSEQ;
            }
            else if (n == P4Type.P4_DYNAMIC || n == P4Type.P4_STATIC)
            {
                pOp.p4.z = _p4.z;
                pOp.p4type = P4Type.P4_DYNAMIC;
            }
            else if (n == P4Type.P4_MEM)
            {
                pOp.p4.pMem = _p4.pMem;
                pOp.p4type = P4Type.P4_MEM;
            }
            else if (n == P4Type.P4_INTARRAY)
            {
                pOp.p4.ai = _p4.ai;
                pOp.p4type = P4Type.P4_INTARRAY;
            }
            else if (n == P4Type.P4_SUBPROGRAM)
            {
                pOp.p4.pProgram = _p4.pProgram;
                pOp.p4type = P4Type.P4_SUBPROGRAM;
            }
            else if (n == P4Type.P4_VTAB)
            {
                pOp.p4.pVtab = _p4.pVtab;
                pOp.p4type = P4Type.P4_VTAB;
                sqlite3VtabLock(_p4.pVtab);
                Debug.Assert((_p4.pVtab).db == p.db);
            }
            else if (n < 0)
            {
                pOp.p4.p = _p4.p;
                pOp.p4type = n;
            }
            else
            {
                //if (n == 0) n =  n = Utility.Sqlite3Strlen30(zP4);
                pOp.p4.z = _p4.z; // sqlite3DbStrNDup(p.db, zP4, n);
                pOp.p4type = P4Type.P4_DYNAMIC;
            }
        }

#if !NDEBUG
    /*
** Change the comment on the the most recently coded instruction.  Or
** insert a No-op and add the comment to that new instruction.  This
** makes the code easier to read during debugging.  None of this happens
** in a production build.
*/
    static void sqlite3VdbeComment( Vdbe p, string zFormat, params object[] ap )
    {
      //      va_list ap;
      if ( null == p ) return;
      Debug.Assert( p.nOp > 0 || p.aOp == null );
      Debug.Assert( p.aOp == null || p.aOp[p.nOp - 1].zComment == null /* || p.db.mallocFailed != 0 */);
      if ( p.nOp != 0 )
      {
        string pz;// = p.aOp[p.nOp-1].zComment;
        Custom.VaStart( ap, zFormat );
        //MemPool.DbFree(db, ref pz);
        pz = Print.VMPrintf( p.db, zFormat, ap );
        p.aOp[p.nOp - 1].zComment = pz;
        Custom.VaEnd( ap );
      }
    }
    static void sqlite3VdbeNoopComment( Vdbe p, string zFormat, params object[] ap )
    {
      //va_list ap;
      if ( null == p ) return;
      VdbeAux.VdbeAddOp0( p, OPCode.OP_Noop );
      Debug.Assert( p.nOp > 0 || p.aOp == null );
      Debug.Assert( p.aOp == null || p.aOp[p.nOp - 1].zComment == null /* || p.db.mallocFailed != 0 */);
      if ( p.nOp != 0 )
      {
        string pz; // = p.aOp[p.nOp - 1].zComment;
        Custom.VaStart( ap, zFormat );
        //MemPool.DbFree(db,ref pz);
        pz = Print.VMPrintf( p.db, zFormat, ap );
        p.aOp[p.nOp - 1].zComment = pz;
        Custom.VaEnd( ap );
      }
    }
#else
#endif
        //* NDEBUG */


        /*
** Return the opcode for a given address.  If the address is -1, then
** return the most recently inserted opcode.
**
** If a memory allocation error has occurred prior to the calling of this
** routine, then a pointer to a dummy VdbeOp will be returned.  That opcode
** is readable and writable, but it has no effect.  The return of a dummy
** opcode allows the call to continue functioning after a OOM fault without
** having to check to see if the return from this routine is a valid pointer.
**
** About the #if SQLITE_OMIT_TRACE:  Normally, this routine is never called
** unless p->nOp>0.  This is because in the absense of SQLITE_OMIT_TRACE,
** an OPCode.OP_Trace instruction is always inserted by sqlite3VdbeGet() as soon as
** a new VDBE is created.  So we are free to set addr to p->nOp-1 without
** having to double-check to make sure that the result is non-negative. But
** if SQLITE_OMIT_TRACE is defined, the OPCode.OP_Trace is omitted and we do need to
** check the value of p->nOp-1 before continuing.
*/

        private static VdbeOp sqlite3VdbeGetOp(Vdbe p, int addr)
        {
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_INIT);
            if (addr < 0)
            {
#if SQLITE_OMIT_TRACE
      VdbeOp dummy = new VdbeOp();
if( p.nOp==0 ) return dummy;
#endif
                addr = p.nOp - 1;
            }
            Debug.Assert((addr >= 0 && addr < p.nOp) /* || p.db.mallocFailed != 0 */);
            //if ( p.db.mallocFailed != 0 )
            //{
            //  return dummy;
            //}
            //else
            {
                return p.aOp[addr];
            }
        }

#if !SQLITE_OMIT_EXPLAIN || !NDEBUG || VDBE_PROFILE || SQLITE_DEBUG
        /*
** Compute a string that describes the P4 parameter for an opcode.
** Use zTemp for any required temporary buffer space.
*/

        private static string displayP4(Op pOp, string zBuffer, int nTemp)
        {
            var zTemp = new StringBuilder(100);
            Debug.Assert(nTemp >= 20);
            switch (pOp.p4type)
            {
                case P4Type.P4_KEYINFO_STATIC:
                case P4Type.P4_KEYINFO:
                    {
                        int i, j;
                        KeyInfo pKeyInfo = pOp.p4.pKeyInfo;
                        Print.Snprintf(nTemp, ref zTemp, "keyinfo(%d", pKeyInfo.nField);
                        i = Utility.Sqlite3Strlen30(zTemp);
                        for (j = 0; j < pKeyInfo.nField; j++)
                        {
                            CollSeq pColl = pKeyInfo.aColl[j];
                            if (pColl != null)
                            {
                                int n = Utility.Sqlite3Strlen30(pColl.zName);
                                if (i + n > nTemp)
                                {
                                    zTemp.Append(",..."); // memcpy( &zTemp[i], ",...", 4 );
                                    break;
                                }
                                zTemp.Append(","); // zTemp[i++] = ',';
                                if (pKeyInfo.aSortOrder != null && pKeyInfo.aSortOrder[j] != 0)
                                {
                                    zTemp.Append("-"); // zTemp[i++] = '-';
                                }
                                zTemp.Append(pColl.zName); // memcpy( &zTemp[i], pColl.zName, n + 1 );
                                i += n;
                            }
                            else if (i + 4 < nTemp)
                            {
                                zTemp.Append(",nil"); // memcpy( &zTemp[i], ",nil", 4 );
                                i += 4;
                            }
                        }
                        zTemp.Append(")"); // zTemp[i++] = ')';
                        //zTemp[i] = 0;
                        Debug.Assert(i < nTemp);
                        break;
                    }
                case P4Type.P4_COLLSEQ:
                    {
                        CollSeq pColl = pOp.p4.pColl;
                        Print.Snprintf(nTemp, ref zTemp, "collseq(%.20s)", (pColl != null ? pColl.zName : "null"));
                        break;
                    }
                case P4Type.P4_FUNCDEF:
                    {
                        FuncDef pDef = pOp.p4.pFunc;
                        Print.Snprintf(nTemp, ref zTemp, "%s(%d)", pDef.zName, pDef.nArg);
                        break;
                    }
                case P4Type.P4_INT64:
                    {
                        Print.Snprintf(nTemp, ref zTemp, "%lld", pOp.p4.pI64);
                        break;
                    }
                case P4Type.P4_INT32:
                    {
                        Print.Snprintf(nTemp, ref zTemp, "%d", pOp.p4.i);
                        break;
                    }
                case P4Type.P4_REAL:
                    {
                        Print.Snprintf(nTemp, ref zTemp, "%.16g", pOp.p4.pReal);
                        break;
                    }
                case P4Type.P4_MEM:
                    {
                        Mem pMem = pOp.p4.pMem;
                        Debug.Assert((pMem.flags & MEMFlag.MEM_Null) == 0);
                        if ((pMem.flags & MEMFlag.MEM_Str) != 0)
                        {
                            zTemp.Append(pMem.z);
                        }
                        else if ((pMem.flags & MEMFlag.MEM_Int) != 0)
                        {
                            Print.Snprintf(nTemp, ref zTemp, "%lld", pMem.u.i);
                        }
                        else if ((pMem.flags & MEMFlag.MEM_Real) != 0)
                        {
                            Print.Snprintf(nTemp, ref zTemp, "%.16g", pMem.r);
                        }
                        else
                        {
                            Debug.Assert((pMem.flags & MEMFlag.MEM_Blob) != 0);
                            zTemp = new StringBuilder("(blob)");
                        }
                        break;
                    }
#if ! SQLITE_OMIT_VIRTUALTABLE
case P4Type.P4_VTAB: {
sqlite3_vtab pVtab = pOp.p4.pVtab.pVtab;
Print.Snprintf(nTemp, ref zTemp, "vtab:%p:%p", pVtab, pVtab.pModule);
break;
}
#endif
                case P4Type.P4_INTARRAY:
                    {
                        Print.Snprintf(nTemp, ref zTemp, "intarray");
                        break;
                    }
                case P4Type.P4_SUBPROGRAM:
                    {
                        Print.Snprintf(nTemp, ref zTemp, "program");
                        break;
                    }
                default:
                    {
                        if (pOp.p4.z != null) zTemp.Append(pOp.p4.z);
                        //if ( zTemp == null )
                        //{
                        //  zTemp = "";
                        //}
                        break;
                    }
            }
            Debug.Assert(zTemp != null);
            return zTemp.ToString();
        }
#endif

        /*
** Declare to the Vdbe that the BTree object at db.aDb[i] is used.
*/

        public static void VdbeUsesBtree(Vdbe p, int i)
        {
            int mask;
            Debug.Assert(i >= 0 && i < p.db.nDb && i < sizeof(uint) * 8);
            Debug.Assert(i < sizeof(int) * 8);
            mask = (int)(1) << i;
            if ((p.btreeMask & mask) == 0)
            {
                p.btreeMask |= mask;
                sqlite3BtreeMutexArrayInsert(p.aMutex, p.db.aDb[i].pBt);
            }
        }


#if VDBE_PROFILE || SQLITE_DEBUG
        /*
** Print a single opcode.  This routine is used for debugging only.
*/

        public static void VdbePrintOp(FILE pOut, int pc, Op pOp)
        {
            string zP4;
            string zPtr = null;
            string zFormat1 = "%4d %-13s %4d %4d %4d %-4s %.2X %s\n";
            if (pOut == null) pOut = Console.Out;
            zP4 = displayP4(pOp, zPtr, 50);
            string zOut = "";
            Print.Snprintf(999, ref zOut, zFormat1, pc,
                             sqlite3OpcodeName(pOp.opcode), pOp.p1, pOp.p2, pOp.p3, zP4, pOp.p5,
#if  SQLITE_DEBUG
 pOp.zComment != null ? pOp.zComment : ""
#else
""
#endif
);
            pOut.Write(zOut);
            //fflush(pOut);
        }
#endif

        /*
** Release an array of N Mem elements
*/

        private static void releaseMemArray(Mem[] p, int N)
        {
            releaseMemArray(p, 0, N);
        }

        private static void releaseMemArray(Mem[] p, int starting, int N)
        {
            if (p != null && p.Length > starting && p[starting] != null && N != 0)
            {
                Mem pEnd;
                sqlite3 db = p[starting].db;
                //byte malloc_failed =  db.mallocFailed;
                for (int i = starting; i < N; i++) //pEnd =  p[N] ; p < pEnd ; p++ )
                {
                    pEnd = p[i];
                    Debug.Assert( //( p[1] ) == pEnd ||
                        N == 1 || p[starting].db == p[starting + 1].db);

                    /* This block is really an inlined version of sqlite3VdbeMemRelease()
          ** that takes advantage of the fact that the memory cell value is
          ** being set to NULL after releasing any dynamic resources.
          **
          ** The justification for duplicating code is that according to
          ** callgrind, this causes a certain test case to hit the CPU 4.7
          ** percent less (x86 linux, gcc version 4.1.2, -O6) than if
          ** sqlite3MemRelease() were called from here. With -O2, this jumps
          ** to 6.6 percent. The test case is inserting 1000 rows into a table
          ** with no indexes using a single prepared INSERT statement, bind()
          ** and reset(). Inserts are grouped into a transaction.
          */
                    if (pEnd != null)
                    {
                        if ((pEnd.flags & (MEMTermFlag.MEM_Agg | MEMTermFlag.MEM_Dyn | MEMFlag.MEM_Frame | MEMFlag.MEM_RowSet)) != 0)
                        {
                            sqlite3VdbeMemRelease(pEnd);
                        }
                        //else if ( pEnd.zMalloc != null )
                        //{
                        //  MemPool.DbFree( db, ref pEnd.zMalloc );
                        //  pEnd.zMalloc = 0;
                        //}
                        pEnd.z = null;
                        pEnd.n = 0;
                        pEnd.flags = MEMFlag.MEM_Null;
                        Malloc.sqlite3_free(ref pEnd._Mem);
                        Malloc.sqlite3_free(ref pEnd.zBLOB);
                    }
                }
                //        db.mallocFailed = malloc_failed;
            }
        }

        /*
    ** Delete a VdbeFrame object and its contents. VdbeFrame objects are
    ** allocated by the OPCode.OP_Program opcode in sqlite3VdbeExec().
    */

        public static void VdbeFrameDelete(VdbeFrame p)
        {
            int i;
            //Mem[] aMem = VdbeFrameMem(p);
            VdbeCursor[] apCsr = p.aChildCsr; // (VdbeCursor)aMem[p.nChildMem];
            for (i = 0; i < p.nChildCsr; i++)
            {
                VdbeFreeCursor(p.v, apCsr[i]);
            }
            releaseMemArray(p.aChildMem, p.nChildMem);
            p = null; // MemPool.DbFree( p.v.db, p );
        }

#if ! SQLITE_OMIT_EXPLAIN
        /*
** Give a listing of the program in the virtual machine.
**
** The interface is the same as sqlite3VdbeExec().  But instead of
** running the code, it invokes the callback once for each instruction.
** This feature is used to implement "EXPLAIN".
**
** When p.explain==1, each instruction is listed.  When
** p.explain==2, only OPCode.OP_Explain instructions are listed and these
** are shown in a different format.  p.explain==2 is used to implement
** EXPLAIN QUERY PLAN.
**
** When p->explain==1, first the main program is listed, then each of
** the trigger subprograms are listed one by one.
*/

        public static int VdbeList(
            Vdbe p /* The VDBE */
            )
        {
            int nRow; /* Stop when row count reaches this */
            int nSub = 0; /* Number of sub-vdbes seen so far */
            SubProgram[] apSub = null; /* Array of sub-vdbes */
            Mem pSub = null; /* Memory cell hold array of subprogs */
            sqlite3 db = p.db; /* The database connection */
            int i; /* Loop counter */
            int rc = StatusCode.SQLITE_OK; /* Return code */
            if (p.pResultSet == null)
                p.pResultSet = new Mem[0]; //Mem* pMem = p.pResultSet = p.aMem[1];   /* First Mem of result set */
            Mem pMem;
            Debug.Assert(p.explain != 0);
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_RUN);
            Debug.Assert(p.rc == StatusCode.SQLITE_OK || p.rc == StatusCode.SQLITE_BUSY || p.rc == StatusCode.SQLITE_NOMEM);
            /* Even though this opcode does not use dynamic strings for
      ** the result, result columns may become dynamic if the user calls
      ** sqlite3_column_text16(), causing a translation to UTF-16 encoding.
      */
            releaseMemArray(p.pResultSet, 8);

            //if ( p.rc == StatusCode.SQLITE_NOMEM )
            //{
            //  /* This happens if a malloc() inside a call to sqlite3_column_text() or
            //  ** sqlite3_column_text16() failed.  */
            //  db.mallocFailed = 1;
            //  return StatusCode.SQLITE_ERROR;
            //}

            /* When the number of output rows reaches nRow, that means the
     ** listing has finished and sqlite3_step() should return StatusCode.SQLITE_DONE.
     ** nRow is the sum of the number of rows in the main program, plus
     ** the sum of the number of rows in all trigger subprograms encountered
     ** so far.  The nRow value will increase as new trigger subprograms are
     ** encountered, but p->pc will eventually catch up to nRow.
     */
            nRow = p.nOp;
            int i_pMem;
            if (p.explain == 1)
            {
                /* The first 8 memory cells are used for the result set.  So we will
        ** commandeer the 9th cell to use as storage for an array of pointers
        ** to trigger subprograms.  The VDBE is guaranteed to have at least 9
        ** cells.  */
                Debug.Assert(p.nMem > 9);
                pSub = p.aMem[9];
                if ((pSub.flags & MEMFlag.MEM_Blob) != 0)
                {
                    /* On the first call to sqlite3_step(), pSub will hold a NULL.  It is
          ** initialized to a BLOB by the P4Type.P4_SUBPROGRAM processing logic below */
                    apSub = p.aMem[9]._SubProgram; //    apSub = (SubProgram**)pSub->z;
                    nSub = apSub.Length; // pSub->n / sizeof( Vdbe* );
                }
                for (i = 0; i < nSub; i++)
                {
                    nRow += apSub[i].nOp;
                }
            }

            i_pMem = 0;
            if (i_pMem >= p.pResultSet.Length) Array.Resize(ref p.pResultSet, 8 + p.pResultSet.Length);
            {
                p.pResultSet[i_pMem] = Malloc.sqlite3Malloc(p.pResultSet[i_pMem]);
            }
            pMem = p.pResultSet[i_pMem++];
            do
            {
                i = p.pc++;
            } while (i < nRow && p.explain == 2 && p.aOp[i].opcode != OPCode.OP_Explain);
            if (i >= nRow)
            {
                p.rc = StatusCode.SQLITE_OK;
                rc = StatusCode.SQLITE_DONE;
            }
            else if (db.u1.isInterrupted)
            {
                p.rc = StatusCode.SQLITE_INTERRUPT;
                rc = StatusCode.SQLITE_ERROR;
                Malloc.SetString(ref p.zErrMsg, db, Utility.ErrorString(p.rc));
            }
            else
            {
                string z;
                Op pOp;
                if (i < p.nOp)
                {
                    /* The output line number is small enough that we are still in the
          ** main program. */
                    pOp = p.aOp[i];
                }
                else
                {
                    /* We are currently listing subprograms.  Figure out which one and
          ** pick up the appropriate opcode. */
                    int j;
                    i -= p.nOp;
                    for (j = 0; i >= apSub[j].nOp; j++)
                    {
                        i -= apSub[j].nOp;
                    }
                    pOp = apSub[j].aOp[i];
                }
                if (p.explain == 1)
                {
                    pMem.flags = MEMFlag.MEM_Int;
                    pMem.type = FundamentalDataType.SQLITE_INTEGER;
                    pMem.u.i = i; /* Program counter */
                    if (p.pResultSet[i_pMem] == null)
                    {
                        p.pResultSet[i_pMem] = Malloc.sqlite3Malloc(p.pResultSet[i_pMem]);
                    }
                    pMem = p.pResultSet[i_pMem++]; //pMem++;

                    /* When an OPCode.OP_Program opcode is encounter (the only opcode that has
          ** a P4Type.P4_SUBPROGRAM argument), expand the size of the array of subprograms
          ** kept in p->aMem[9].z to hold the new program - assuming this subprogram
          ** has not already been seen.
          */
                    pMem.flags = MEMTermFlag.MEM_Static | MEMFlag.MEM_Str | MEMTermFlag.MEM_Term;
                    pMem.z = sqlite3OpcodeName(pOp.opcode); /* Opcode */
                    Debug.Assert(pMem.z != null);
                    pMem.n = Utility.Sqlite3Strlen30(pMem.z);
                    pMem.type = FundamentalDataType.SQLITE_TEXT;
                    pMem.enc = Const.SQLITE_UTF8;
                    if (p.pResultSet[i_pMem] == null)
                    {
                        p.pResultSet[i_pMem] = Malloc.sqlite3Malloc(p.pResultSet[i_pMem]);
                    }
                    pMem = p.pResultSet[i_pMem++]; //pMem++;
                    if (pOp.p4type == P4Type.P4_SUBPROGRAM)
                    {
                        //Debugger.Break(); // TODO
                        //int nByte = 0;//(nSub+1)*sizeof(SubProgram*);
                        int j;
                        for (j = 0; j < nSub; j++)
                        {
                            if (apSub[j] == pOp.p4.pProgram) break;
                        }
                        if (j == nSub)
                        {
                            // && StatusCode.SQLITE_OK==sqlite3VdbeMemGrow(pSub, nByte, 1) ){
                            Array.Resize(ref apSub, nSub + 1);
                            pSub._SubProgram = apSub; // (SubProgram)pSub.z;
                            apSub[nSub++] = pOp.p4.pProgram;
                            pSub.flags |= MEMFlag.MEM_Blob;
                            pSub.n = 0; //nSub*sizeof(SubProgram*);
                        }
                    }
                }

                pMem.flags = MEMFlag.MEM_Int;
                pMem.u.i = pOp.p1; /* P1 */
                pMem.type = FundamentalDataType.SQLITE_INTEGER;
                if (p.pResultSet[i_pMem] == null)
                {
                    p.pResultSet[i_pMem] = Malloc.sqlite3Malloc(p.pResultSet[i_pMem]);
                }
                pMem = p.pResultSet[i_pMem++]; //pMem++;

                pMem.flags = MEMFlag.MEM_Int;
                pMem.u.i = pOp.p2; /* P2 */
                pMem.type = FundamentalDataType.SQLITE_INTEGER;
                if (p.pResultSet[i_pMem] == null)
                {
                    p.pResultSet[i_pMem] = Malloc.sqlite3Malloc(p.pResultSet[i_pMem]);
                }
                pMem = p.pResultSet[i_pMem++]; //pMem++;

                if (p.explain == 1)
                {
                    pMem.flags = MEMFlag.MEM_Int;
                    pMem.u.i = pOp.p3; /* P3 */
                    pMem.type = FundamentalDataType.SQLITE_INTEGER;
                    if (p.pResultSet[i_pMem] == null)
                    {
                        p.pResultSet[i_pMem] = Malloc.sqlite3Malloc(p.pResultSet[i_pMem]);
                    }
                    pMem = p.pResultSet[i_pMem++]; //pMem++;
                }

                //if ( sqlite3VdbeMemGrow( pMem, 32, 0 ) != 0 )
                //{                                                     /* P4 */
                //  Debug.Assert( p.db.mallocFailed != 0 );
                //  return StatusCode.SQLITE_ERROR;
                //}
                pMem.flags = MEMTermFlag.MEM_Dyn | MEMFlag.MEM_Str | MEMTermFlag.MEM_Term;
                z = displayP4(pOp, pMem.z, 32);
                if (z != pMem.z)
                {
                    sqlite3VdbeMemSetStr(pMem, z, -1, Const.SQLITE_UTF8, null);
                }
                else
                {
                    Debug.Assert(pMem.z != null);
                    pMem.n = Utility.Sqlite3Strlen30(pMem.z);
                    pMem.enc = Const.SQLITE_UTF8;
                }
                pMem.type = FundamentalDataType.SQLITE_TEXT;
                if (p.pResultSet[i_pMem] == null)
                {
                    p.pResultSet[i_pMem] = Malloc.sqlite3Malloc(p.pResultSet[i_pMem]);
                }
                pMem = p.pResultSet[i_pMem++]; //pMem++;

                if (p.explain == 1)
                {
                    //if ( sqlite3VdbeMemGrow( pMem, 4, 0 ) != 0 )
                    //{
                    //  Debug.Assert( p.db.mallocFailed != 0 );
                    //  return StatusCode.SQLITE_ERROR;
                    //}
                    pMem.flags = MEMTermFlag.MEM_Dyn | MEMFlag.MEM_Str | MEMTermFlag.MEM_Term;
                    pMem.n = 2;
                    pMem.z = pOp.p5.ToString("x2"); //Print.Snprintf( 3, pMem.z, "%.2x", pOp.p5 );   /* P5 */
                    pMem.type = FundamentalDataType.SQLITE_TEXT;
                    pMem.enc = Const.SQLITE_UTF8;
                    if (p.pResultSet[i_pMem] == null)
                    {
                        p.pResultSet[i_pMem] = Malloc.sqlite3Malloc(p.pResultSet[i_pMem]);
                    }
                    pMem = p.pResultSet[i_pMem++]; // pMem++;

#if SQLITE_DEBUG
                    if (pOp.zComment != null)
                    {
                        pMem.flags = MEMFlag.MEM_Str | MEMTermFlag.MEM_Term;
                        pMem.z = pOp.zComment;
                        pMem.n = pMem.z == null ? 0 : Utility.Sqlite3Strlen30(pMem.z);
                        pMem.enc = Const.SQLITE_UTF8;
                        pMem.type = FundamentalDataType.SQLITE_TEXT;
                    }
                    else
#endif
                    {
                        pMem.flags = MEMFlag.MEM_Null; /* Comment */
                        pMem.type = FundamentalDataType.SQLITE_NULL;
                    }
                }

                p.nResColumn = (ushort)(8 - 5 * (p.explain - 1));
                p.rc = StatusCode.SQLITE_OK;
                rc = StatusCode.SQLITE_ROW;
            }
            return rc;
        }
#endif
        // * SQLITE_OMIT_EXPLAIN */

#if  SQLITE_DEBUG
        /*
** Print the SQL that was used to generate a VDBE program.
*/

        public static void VdbePrintSql(Vdbe p)
        {
            int nOp = p.nOp;
            VdbeOp pOp;
            if (nOp < 1) return;
            pOp = p.aOp[0];
            if (pOp.opcode == OPCode.OP_Trace && pOp.p4.z != null)
            {
                string z = pOp.p4.z;
                z = z.Trim(); // while ( SqliteInt.Sqlite3Isspace( *(byte*)z ) ) z++;
                Console.Write("SQL: [%s]\n", z);
            }
        }
#endif

#if !SQLITE_OMIT_TRACE && SQLITE_ENABLE_IOTRACE
/*
** Print an IOTRACE message showing SQL content.
*/
static void sqlite3VdbeIOTraceSql( Vdbe p )
{
int nOp = p.nOp;
VdbeOp pOp;
if ( SQLite3IoTrace == false ) return;
if ( nOp < 1 ) return;
pOp = p.aOp[0];
if ( pOp.opcode == OPCode.OP_Trace && pOp.p4.z != null )
{
int i, j;
string z = "";//char z[1000];
Print.Snprintf( 1000, ref  z, "%s", pOp.p4.z );
//for(i=0; SqliteInt.Sqlite3Isspace(z[i]); i++){}
//for(j=0; z[i]; i++){
//if( SqliteInt.Sqlite3Isspace(z[i]) ){
//if( z[i-1]!=' ' ){
//z[j++] = ' ';
//}
//}else{
//z[j++] = z[i];
//}
//}
//z[j] = 0;
//z = z.Trim( z );
sqlite3IoTrace( "SQL %s\n", z.Trim() );
}
}
#endif
        // * !SQLITE_OMIT_TRACE  && SQLITE_ENABLE_IOTRACE */

        /*
** Allocate space from a fixed size buffer and return a pointer to
** that space.  If insufficient space is available, return NULL.
**
** The pBuf parameter is the initial value of a pointer which will
** receive the new memory.  pBuf is normally NULL.  If pBuf is not
** NULL, it means that memory space has already been allocated and that
** this routine should not allocate any new memory.  When pBuf is not
** NULL simply return pBuf.  Only allocate new memory space when pBuf
** is NULL.
**
** nByte is the number of bytes of space needed.
**
** *ppFrom points to available space and pEnd points to the end of the
** available space.  When space is allocated, *ppFrom is advanced past
** the end of the allocated space.
**
** *pnByte is a counter of the number of bytes of space that have failed
** to allocate.  If there is insufficient space in *ppFrom to satisfy the
** request, then increment *pnByte by the amount of the request.
*/
        //static void* allocSpace(
        //  void* pBuf,          /* Where return pointer will be stored */
        //  int nByte,           /* Number of bytes to allocate */
        //  byte** ppFrom,         /* IN/OUT: Allocate from *ppFrom */
        //  byte* pEnd,            /* Pointer to 1 byte past the end of *ppFrom buffer */
        //  int* pnByte          /* If allocation cannot be made, increment *pnByte */
        //)
        //{
        //  assert(EIGHT_BYTE_ALIGNMENT(*ppFrom));
        //  if (pBuf) return pBuf;
        //  nByte = Utility.ROUND8(nByte);
        //  if (&(*ppFrom)[nByte] <= pEnd)
        //  {
        //    pBuf = (void*)*ppFrom;
        //    *ppFrom += nByte;
        //  }
        //  else
        //  {
        //    *pnByte += nByte;
        //  }
        //  return pBuf;
        //}

        /*
    ** Prepare a virtual machine for execution.  This involves things such
    ** as allocating stack space and initializing the program counter.
    ** After the VDBE has be prepped, it can be executed by one or more
    ** calls to sqlite3VdbeExec().
    **
    ** This is the only way to move a VDBE from VdbeMagic.VDBE_MAGIC_INIT to
    ** VdbeMagic.VDBE_MAGIC_RUN.
    **
    ** This function may be called more than once on a single virtual machine.
    ** The first call is made while compiling the SQL statement. Subsequent
    ** calls are made as part of the process of resetting a statement to be
    ** re-executed (from a call to sqlite3_reset()). The nVar, nMem, nCursor
    ** and isExplain parameters are only passed correct values the first time
    ** the function is called. On subsequent calls, from sqlite3_reset(), nVar
    ** is passed -1 and nMem, nCursor and isExplain are all passed zero.
    */

        public static void VdbeMakeReady(
            Vdbe p, /* The VDBE */
            int nVar, /* Number of '?' see in the SQL statement */
            int nMem, /* Number of memory cells to allocate */
            int nCursor, /* Number of cursors to allocate */
            int nArg, /* Maximum number of args in SubPrograms */
            int isExplain, /* True if the EXPLAIN keywords is present */
            int usesStmtJournal /* True to set Vdbe.usesStmtJournal */
            )
        {
            int n;
            sqlite3 db = p.db;

            Debug.Assert(p != null);
            Debug.Assert(p.magic == VdbeMagic.VDBE_MAGIC_INIT);

            /* There should be at least one opcode.
      */
            Debug.Assert(p.nOp > 0);

            /* Set the magic to VdbeMagic.VDBE_MAGIC_RUN sooner rather than later. */
            p.magic = VdbeMagic.VDBE_MAGIC_RUN;

            /* For each cursor required, also allocate a memory cell. Memory
      ** cells (nMem+1-nCursor)..nMem, inclusive, will never be used by
      ** the vdbe program. Instead they are used to allocate space for
      ** VdbeCursor/BtCursor structures. The blob of memory associated with
      ** cursor 0 is stored in memory cell nMem. Memory cell (nMem-1)
      ** stores the blob of memory associated with cursor 1, etc.
      **
      ** See also: allocateCursor().
      */
            nMem += nCursor;

            /* Allocate space for memory registers, SQL variables, VDBE cursors and
      ** an array to marshal SQL function arguments in. This is only done the
      ** first time this function is called for a given VDBE, not when it is
      ** being called from sqlite3_reset() to reset the virtual machine.
      */
            if (nVar >= 0 /* &&  UnitTest.ALWAYS(db->mallocFailed==0) */)
            {
                //byte* zCsr = (byte*)&p->aOp[p->nOp];       /* Memory avaliable for alloation */
                //byte* zEnd = (byte*)&p->aOp[p->nOpAlloc];  /* First byte past available mem */
                int nByte; /* How much extra memory needed */
                resolveP2Values(p, ref nArg);
                p.usesStmtJournal = usesStmtJournal != 0;
                if (isExplain != 0 && nMem < 10)
                {
                    nMem = 10;
                }
                //memset(zCsr, 0, zEnd-zCsr);
                //zCsr += ( zCsr - (byte*)0 ) & 7;
                //assert( EIGHT_BYTE_ALIGNMENT( zCsr ) );
                //
                // C# -- Replace allocation with individual Dims
                //
                /* Memory for registers, parameters, cursor, etc, is allocated in two
        ** passes.  On the first pass, we try to reuse unused space at the 
        ** end of the opcode array.  If we are unable to satisfy all memory
        ** requirements by reusing the opcode array tail, then the second
        ** pass will fill in the rest using a fresh allocation.  
        **
        ** This two-pass approach that reuses as much memory as possible from
        ** the leftover space at the end of the opcode array can significantly
        ** reduce the amount of memory held by a prepared statement.
        */
                //do
                //{
                //  nByte = 0;
                //  p->aMem = allocSpace(p->aMem, nMem * sizeof(Mem), &zCsr, zEnd, &nByte);
                //  p->aVar = allocSpace(p->aVar, nVar * sizeof(Mem), &zCsr, zEnd, &nByte);
                //  p->apArg = allocSpace(p->apArg, nArg * sizeof(Mem*), &zCsr, zEnd, &nByte);
                //  p->azVar = allocSpace(p->azVar, nVar * sizeof(char*), &zCsr, zEnd, &nByte);
                //  p->apCsr = allocSpace(p->apCsr, nCursor * sizeof(VdbeCursor*),
                //                        &zCsr, zEnd, &nByte);
                //  if (nByte)
                //  {
                //    p->pFree = Malloc.DbMallocZero(db, nByte);
                //  }
                //  zCsr = p->pFree;
                //  zEnd = &zCsr[nByte];
                //} while (nByte && !db->mallocFailed);


                // C# -- Replace allocation with individual Dims
                // aMem is 1 based, so allocate 1 extra cell under C#
                p.aMem = new Mem[nMem + 1];
                for (n = 0; n <= nMem; n++)
                {
                    p.aMem[n] = Malloc.sqlite3Malloc(p.aMem[n]);
                    p.aMem[n].db = db;
                }
                //p.aMem--;         /* aMem[] goes from 1..nMem */
                p.nMem = nMem; /*       not from 0..nMem-1 */
                //
                p.aVar = new Mem[nVar == 0 ? 1 : nVar];
                for (n = 0; n < nVar; n++)
                {
                    p.aVar[n] = Malloc.sqlite3Malloc(p.aVar[n]);
                }
                p.nVar = (ynVar)nVar;
                p.okVar = 0;
                //
                p.apArg = new Mem[nArg == 0 ? 1 : nArg]; //p.apArg = (Mem**)p.aVar[nVar];
                //

                p.azVar = new string[nArg == 0 ? 1 : nArg]; //p.azVar = (char**)p.apArg[nArg];
                for (n = 0; n < nArg; n++)
                {
                    p.azVar[n] = "";
                }
                //
                p.apCsr = new VdbeCursor[nCursor == 0 ? 1 : nCursor]; //p.apCsr = (VdbeCursor**)p.azVar[nVar];
                p.apCsr[0] = new VdbeCursor();
                p.nCursor = (ushort)nCursor;
                if (p.aVar != null)
                {
                    p.nVar = (ynVar)nVar;
                    //
                    for (n = 0; n < nVar; n++)
                    {
                        p.aVar[n].flags = MEMFlag.MEM_Null;
                        p.aVar[n].db = db;
                    }
                }
                if (p.aMem != null)
                {
                    //p.aMem--;                    /* aMem[] goes from 1..nMem */
                    p.nMem = nMem; /*       not from 0..nMem-1 */
                    for (n = 0; n <= nMem; n++)
                    {
                        p.aMem[n].flags = MEMFlag.MEM_Null;
                        p.aMem[n].n = 0;
                        p.aMem[n].z = null;
                        p.aMem[n].zBLOB = null;
                        p.aMem[n].db = db;
                    }
                }
            }

#if  SQLITE_DEBUG
            for (n = 1; n < p.nMem; n++)
            {
                Debug.Assert(p.aMem[n].db == db);
            }
#endif

            p.pc = -1;
            p.rc = StatusCode.SQLITE_OK;
            p.errorAction = OnConstraintError.OE_Abort;
            p.explain |= isExplain;
            p.magic = VdbeMagic.VDBE_MAGIC_RUN;
            p.nChange = 0;
            p.cacheCtr = 1;
            p.minWriteFileFormat = 255;
            p.iStatement = 0;
#if  VDBE_PROFILE
{
int i;
for ( i = 0 ; i < p.nOp ; i++ )
{
p.aOp[i].cnt = 0;
p.aOp[i].cycles = 0;
}
}
#endif
        }

        /*
    ** Close a VDBE cursor and release all the resources that cursor
    ** happens to hold.
    */

        public static void VdbeFreeCursor(Vdbe p, VdbeCursor pCx)
        {
            if (pCx == null)
            {
                return;
            }

            if (pCx.pBt != null)
            {
                sqlite3BtreeClose(ref pCx.pBt);
                /* The pCx.pCursor will be close automatically, if it exists, by
        ** the call above. */
            }
            else if (pCx.pCursor != null)
            {
                sqlite3BtreeCloseCursor(pCx.pCursor);
                MemPool.MemFreeBtCursor(ref pCx.pCursor);
            }
#if ! SQLITE_OMIT_VIRTUALTABLE
if( pCx.pVtabCursor ){
sqlite3_vtab_cursor pVtabCursor = pCx.pVtabCursor;
const sqlite3_module pModule = pCx.pModule;
p.inVtabMethod = 1;
pModule.xClose(pVtabCursor);
p.inVtabMethod = 0;
}
#endif
        }

        /*
    ** Copy the values stored in the VdbeFrame structure to its Vdbe. This
    ** is used, for example, when a trigger sub-program is halted to restore
    ** control to the main program.
    */

        public static int VdbeFrameRestore(VdbeFrame pFrame)
        {
            Vdbe v = pFrame.v;
            v.aOp = pFrame.aOp;
            v.nOp = pFrame.nOp;
            v.aMem = pFrame.aMem;
            v.nMem = pFrame.nMem;
            v.apCsr = pFrame.apCsr;
            v.nCursor = pFrame.nCursor;
            v.db.lastRowid = pFrame.lastRowid;
            v.nChange = pFrame.nChange;
            return pFrame.pc;
        }

        /*
    ** Close all cursors.
    **
    ** Also release any dynamic memory held by the VM in the Vdbe.aMem memory 
    ** cell array. This is necessary as the memory cell array may contain
    ** pointers to VdbeFrame objects, which may in turn contain pointers to
    ** open cursors.
    */

        private static void closeAllCursors(Vdbe p)
        {
            if (p.pFrame != null)
            {
                VdbeFrame pFrame = p.pFrame;
                for (pFrame = p.pFrame; pFrame.pParent != null; pFrame = pFrame.pParent) ;
                VdbeFrameRestore(pFrame);
            }
            p.pFrame = null;
            p.nFrame = 0;

            if (p.apCsr != null)
            {
                int i;
                for (i = 0; i < p.nCursor; i++)
                {
                    VdbeCursor pC = p.apCsr[i];
                    if (pC != null)
                    {
                        VdbeFreeCursor(p, pC);
                        p.apCsr[i] = null;
                    }
                }
            }
            if (p.aMem != null)
            {
                releaseMemArray(p.aMem, 1, p.nMem);
            }
        }

        /*
    ** Clean up the VM after execution.
    **
    ** This routine will automatically close any cursors, lists, and/or
    ** sorters that were left open.  It also deletes the values of
    ** variables in the aVar[] array.
    */

        private static void Cleanup(Vdbe p)
        {
            sqlite3 db = p.db;
#if SQLITE_DEBUG
            /* Execute assert() statements to ensure that the Vdbe.apCsr[] and 
  ** Vdbe.aMem[] arrays have already been cleaned up.  */
            int i;
            //TODO for(i=0; i<p.nCursor; i++) Debug.Assert( p.apCsr==null || p.apCsr[i]==null );
            for (i = 1; i <= p.nMem; i++) Debug.Assert(p.aMem != null || p.aMem[i].flags == MEMFlag.MEM_Null);
#endif

            MemPool.DbFree(db, ref p.zErrMsg);
            p.pResultSet = null;
        }

        /*
    ** Set the number of result columns that will be returned by this SQL
    ** statement. This is now set at compile time, rather than during
    ** execution of the vdbe program so that sqlite3_column_count() can
    ** be called on an SQL statement before sqlite3_step().
    */

        public static void VdbeSetNumCols(Vdbe p, int nResColumn)
        {
            Mem pColName;
            int n;
            sqlite3 db = p.db;

            releaseMemArray(p.aColName, p.nResColumn * VdbeColnameType.COLNAME_N);
            MemPool.DbFree(db, ref p.aColName);
            n = nResColumn * VdbeColnameType.COLNAME_N;
            p.nResColumn = (ushort)nResColumn;
            p.aColName = new Mem[n]; // (Mem*)Malloc.DbMallocZero(db, Mem.Length * n);
            //if (p.aColName == 0) return;
            while (n-- > 0)
            {
                p.aColName[n] = Malloc.sqlite3Malloc(p.aColName[n]);
                pColName = p.aColName[n];
                pColName.flags = MEMFlag.MEM_Null;
                pColName.db = p.db;
            }
        }

        /*
    ** Set the name of the idx'th column to be returned by the SQL statement.
    ** zName must be a pointer to a nul terminated string.
    **
    ** This call must be made after a call to VdbeAux.VdbeSetNumCols().
    **
    ** The final parameter, xDel, must be one of Const.SQLITE_DYNAMIC, Const.SQLITE_STATIC
    ** or Const.SQLITE_TRANSIENT. If it is Const.SQLITE_DYNAMIC, then the buffer pointed
    ** to by zName will be freed by MemPool.DbFree() when the vdbe is destroyed.
    */


        public static int VdbeSetColName(
            Vdbe p, /* Vdbe being configured */
            int idx, /* Index of column zName applies to */
            int var, /* One of the COLNAME_* constants */
            string zName, /* Pointer to buffer containing name */
            dxDel xDel /* Memory management strategy for zName */
            )
        {
            int rc;
            Mem pColName;
            Debug.Assert(idx < p.nResColumn);
            Debug.Assert(var < VdbeColnameType.COLNAME_N);
            //if ( p.db.mallocFailed != 0 )
            //{
            //  Debug.Assert( null == zName || xDel != Const.SQLITE_DYNAMIC );
            //  return StatusCode.SQLITE_NOMEM;
            //}
            Debug.Assert(p.aColName != null);
            pColName = p.aColName[idx + var * p.nResColumn];
            rc = sqlite3VdbeMemSetStr(pColName, zName, -1, Const.SQLITE_UTF8, xDel);
            Debug.Assert(rc != 0 || null == zName || (pColName.flags & MEMTermFlag.MEM_Term) != 0);
            return rc;
        }

        /*
    ** A read or write transaction may or may not be active on database handle
    ** db. If a transaction is active, commit it. If there is a
    ** write-transaction spanning more than one database file, this routine
    ** takes care of the master journal trickery.
    */

        private static int vdbeCommit(sqlite3 db, Vdbe p)
        {
            int i;
            int nTrans = 0; /* Number of databases with an active write-transaction */
            int rc = StatusCode.SQLITE_OK;
            bool needXcommit = false;

#if SQLITE_OMIT_VIRTUALTABLE
            /* With this option, sqlite3VtabSync() is defined to be simply
** StatusCode.SQLITE_OK so p is not used.
*/
            Helper.UNUSED_PARAMETER(p);
#endif
            /* Before doing anything else, call the xSync() callback for any
** virtual module tables written in this transaction. This has to
** be done before determining whether a master journal file is
** required, as an xSync() callback may add an attached database
** to the transaction.
*/
            rc = sqlite3VtabSync(db, p.zErrMsg);
            if (rc != StatusCode.SQLITE_OK)
            {
                return rc;
            }

            /* This loop determines (a) if the commit hook should be invoked and
      ** (b) how many database files have open write transactions, not
      ** including the temp database. (b) is important because if more than
      ** one database file has an open write transaction, a master journal
      ** file is required for an atomic commit.
      */
            for (i = 0; i < db.nDb; i++)
            {
                Btree pBt = db.aDb[i].pBt;
                if (sqlite3BtreeIsInTrans(pBt))
                {
                    needXcommit = true;
                    if (i != 1) nTrans++;
                }
            }

            /* If there are any write-transactions at all, invoke the commit hook */
            if (needXcommit && db.xCommitCallback != null)
            {
                rc = db.xCommitCallback(db.pCommitArg);
                if (rc != 0)
                {
                    return StatusCode.SQLITE_CONSTRAINT;
                }
            }

            /* The simple case - no more than one database file (not counting the
      ** TEMP database) has a transaction active.   There is no need for the
      ** master-journal.
      **
      ** If the return value of sqlite3BtreeGetFilename() is a zero length
      ** string, it means the main database is :memory: or a temp file.  In
      ** that case we do not support atomic multi-file commits, so use the
      ** simple case then too.
      */
            if (0 == Utility.Sqlite3Strlen30(sqlite3BtreeGetFilename(db.aDb[0].pBt))
                || nTrans <= 1)
            {
                for (i = 0; rc == StatusCode.SQLITE_OK && i < db.nDb; i++)
                {
                    Btree pBt = db.aDb[i].pBt;
                    if (pBt != null)
                    {
                        rc = sqlite3BtreeCommitPhaseOne(pBt, null);
                    }
                }

                /* Do the commit only if all databases successfully complete phase 1.
        ** If one of the BtreeCommitPhaseOne() calls fails, this indicates an
        ** IO error while deleting or truncating a journal file. It is Utility.Unlikely,
        ** but could happen. In this case abandon processing and return the error.
        */
                for (i = 0; rc == StatusCode.SQLITE_OK && i < db.nDb; i++)
                {
                    Btree pBt = db.aDb[i].pBt;
                    if (pBt != null)
                    {
                        rc = sqlite3BtreeCommitPhaseTwo(pBt);
                    }
                }
                if (rc == StatusCode.SQLITE_OK)
                {
                    sqlite3VtabCommit(db);
                }
            }

                /* The complex case - There is a multi-file write-transaction active.
          ** This requires a master journal file to ensure the transaction is
          ** committed atomicly.
          */
#if ! SQLITE_OMIT_DISKIO
            else
            {
                sqlite3_vfs pVfs = db.pVfs;
                bool needSync = false;
                string zMaster = ""; /* File-name for the master journal */
                string zMainFile = sqlite3BtreeGetFilename(db.aDb[0].pBt);
                sqlite3_file pMaster = null;
                long offset = 0;
                int res = 0;

                /* Select a master journal file name */
                do
                {
                    long iRandom = 0;
                    MemPool.DbFree(db, ref zMaster);
                    Random.Randomness(sizeof(uint), ref iRandom); //random.Length
                    zMaster = Print.MPrintf(db, "%s-mj%08X", zMainFile, iRandom & 0x7fffffff);
                    //if (!zMaster)
                    //{
                    //  return StatusCode.SQLITE_NOMEM;
                    //}
                    rc = sqlite3OsAccess(pVfs, zMaster, AccessFlag.SQLITE_ACCESS_EXISTS, ref res);
                } while (rc == StatusCode.SQLITE_OK && res == 1);
                if (rc == StatusCode.SQLITE_OK)
                {
                    /* Open the master journal. */
                    rc = sqlite3OsOpenMalloc(ref pVfs, zMaster, ref pMaster,
                                             FileOpenOperation.SQLITE_OPEN_READWRITE | FileOpenOperation.SQLITE_OPEN_CREATE |
                                             FileOpenOperation.SQLITE_OPEN_EXCLUSIVE | FileOpenOperation.SQLITE_OPEN_MASTER_JOURNAL, ref rc
                        );
                }
                if (rc != StatusCode.SQLITE_OK)
                {
                    MemPool.DbFree(db, ref zMaster);
                    return rc;
                }

                /* Write the name of each database file in the transaction into the new
        ** master journal file. If an error occurs at this point close
        ** and delete the master journal file. All the individual journal files
        ** still have 'null' as the master journal pointer, so they will roll
        ** back independently if a failure occurs.
        */
                for (i = 0; i < db.nDb; i++)
                {
                    Btree pBt = db.aDb[i].pBt;
                    if (sqlite3BtreeIsInTrans(pBt))
                    {
                        string zFile = sqlite3BtreeGetJournalname(pBt);
                        if (String.IsNullOrEmpty(zFile)) // zFile == 0 || zFile[0] == 0)
                        {
                            continue; /* Ignore TEMP and :memory: databases */
                        }
                        if (!needSync && 0 == sqlite3BtreeSyncDisabled(pBt))
                        {
                            needSync = true;
                        }
                        rc = sqlite3OsWrite(pMaster, Encoding.UTF8.GetBytes(zFile), Utility.Sqlite3Strlen30(zFile), offset);
                        offset += Utility.Sqlite3Strlen30(zFile);
                        if (rc != StatusCode.SQLITE_OK)
                        {
                            sqlite3OsCloseFree(pMaster);
                            sqlite3OsDelete(pVfs, zMaster, 0);
                            MemPool.DbFree(db, ref zMaster);
                            return rc;
                        }
                    }
                }

                /* Sync the master journal file. If the IOCAP_SEQUENTIAL device
        ** flag is set this is not required.
        */
                if (needSync
                    && 0 == (sqlite3OsDeviceCharacteristics(pMaster) & Iocap.SQLITE_IOCAP_SEQUENTIAL)
                    && StatusCode.SQLITE_OK != (rc = sqlite3OsSync(pMaster, SyncTypeFlag.SQLITE_SYNC_NORMAL))
                    )
                {
                    sqlite3OsCloseFree(pMaster);
                    sqlite3OsDelete(pVfs, zMaster, 0);
                    MemPool.DbFree(db, ref zMaster);
                    return rc;
                }

                /* Sync all the db files involved in the transaction. The same call
        ** sets the master journal pointer in each individual journal. If
        ** an error occurs here, do not delete the master journal file.
        **
        ** If the error occurs during the first call to
        ** sqlite3BtreeCommitPhaseOne(), then there is a chance that the
        ** master journal file will be orphaned. But we cannot delete it,
        ** in case the master journal file name was written into the journal
        ** file before the failure occurred.
        */
                for (i = 0; rc == StatusCode.SQLITE_OK && i < db.nDb; i++)
                {
                    Btree pBt = db.aDb[i].pBt;
                    if (pBt != null)
                    {
                        rc = sqlite3BtreeCommitPhaseOne(pBt, zMaster);
                    }
                }
                sqlite3OsCloseFree(pMaster);
                if (rc != StatusCode.SQLITE_OK)
                {
                    MemPool.DbFree(db, ref zMaster);
                    return rc;
                }

                /* Delete the master journal file. This commits the transaction. After
        ** doing this the directory is synced again before any individual
        ** transaction files are deleted.
        */
                rc = sqlite3OsDelete(pVfs, zMaster, 1);
                MemPool.DbFree(db, ref zMaster);
                if (rc != 0)
                {
                    return rc;
                }

                /* All files and directories have already been synced, so the following
        ** calls to sqlite3BtreeCommitPhaseTwo() are only closing files and
        ** deleting or truncating journals. If something goes wrong while
        ** this is happening we don't really care. The integrity of the
        ** transaction is already guaranteed, but some stray 'cold' journals
        ** may be lying around. Returning an error code won't help matters.
        */
#if SQLITE_TEST
        disable_simulated_io_errors();
#endif
                sqlite3BeginBenignMalloc();
                for (i = 0; i < db.nDb; i++)
                {
                    Btree pBt = db.aDb[i].pBt;
                    if (pBt != null)
                    {
                        sqlite3BtreeCommitPhaseTwo(pBt);
                    }
                }
                sqlite3EndBenignMalloc();
#if SQLITE_TEST
        enable_simulated_io_errors();
#endif
                sqlite3VtabCommit(db);
            }
#endif

            return rc;
        }

        /*
    ** This routine checks that the sqlite3.activeVdbeCnt count variable
    ** matches the number of vdbe's in the list sqlite3.pVdbe that are
    ** currently active. An Debug.Assertion fails if the two counts do not match.
    ** This is an internal self-check only - it is not an essential processing
    ** step.
    **
    ** This is a no-op if NDEBUG is defined.
    */
#if !NDEBUG
    static void checkActiveVdbeCnt( sqlite3 db )
    {
      Vdbe p;
      int cnt = 0;
      int nWrite = 0;
      p = db.pVdbe;
      while ( p != null )
      {
        if ( p.magic == VdbeMagic.VDBE_MAGIC_RUN && p.pc >= 0 )
        {
          cnt++;
          if ( p.readOnly == false ) nWrite++;
        }
        p = p.pNext;
      }
      Debug.Assert( cnt == db.activeVdbeCnt );
      Debug.Assert( nWrite == db.writeVdbeCnt );
    }
#else
        //#define checkActiveVdbeCnt(x)
        private static void checkActiveVdbeCnt(sqlite3 db)
        {
        }
#endif

        /*
** For every Btree that in database connection db which
** has been modified, "trip" or invalidate each cursor in
** that Btree might have been modified so that the cursor
** can never be used again.  This happens when a rollback
*** occurs.  We have to trip all the other cursors, even
** cursor from other VMs in different database connections,
** so that none of them try to use the data at which they
** were pointing and which now may have been changed due
** to the rollback.
**
** Remember that a rollback can delete tables complete and
** reorder rootpages.  So it is not sufficient just to save
** the state of the cursor.  We have to invalidate the cursor
** so that it is never used again.
*/

        private static void invalidateCursorsOnModifiedBtrees(sqlite3 db)
        {
            int i;
            for (i = 0; i < db.nDb; i++)
            {
                Btree p = db.aDb[i].pBt;
                if (p != null && sqlite3BtreeIsInTrans(p))
                {
                    sqlite3BtreeTripAllCursors(p, StatusCode.SQLITE_ABORT);
                }
            }
        }

        /*
    ** If the Vdbe passed as the first argument opened a statement-transaction,
    ** close it now. Argument eOp must be either SavePoint.SAVEPOINT_ROLLBACK or
    ** SavePoint.SAVEPOINT_RELEASE. If it is SavePoint.SAVEPOINT_ROLLBACK, then the statement
    ** transaction is rolled back. If eOp is SavePoint.SAVEPOINT_RELEASE, then the
    ** statement transaction is commtted.
    **
    ** If an IO error occurs, an SQLITE_IOERR_XXX error code is returned.
    ** Otherwise StatusCode.SQLITE_OK.
    */

        public static int VdbeCloseStatement(Vdbe p, int eOp)
        {
            sqlite3 db = p.db;
            int rc = StatusCode.SQLITE_OK;
            /* If p->iStatement is greater than zero, then this Vdbe opened a 
      ** statement transaction that should be closed here. The only exception
      ** is that an IO error may have occured, causing an emergency rollback.
      ** In this case (db->nStatement==0), and there is nothing to do.
      */
            if (db.nStatement != 0 && p.iStatement != 0)
            {
                int i;
                int iSavepoint = p.iStatement - 1;

                Debug.Assert(eOp == SavePoint.SAVEPOINT_ROLLBACK || eOp == SavePoint.SAVEPOINT_RELEASE);
                Debug.Assert(db.nStatement > 0);
                Debug.Assert(p.iStatement == (db.nStatement + db.nSavepoint));

                for (i = 0; i < db.nDb; i++)
                {
                    int rc2 = StatusCode.SQLITE_OK;
                    Btree pBt = db.aDb[i].pBt;
                    if (pBt != null)
                    {
                        if (eOp == SavePoint.SAVEPOINT_ROLLBACK)
                        {
                            rc2 = sqlite3BtreeSavepoint(pBt, SavePoint.SAVEPOINT_ROLLBACK, iSavepoint);
                        }
                        if (rc2 == StatusCode.SQLITE_OK)
                        {
                            rc2 = sqlite3BtreeSavepoint(pBt, SavePoint.SAVEPOINT_RELEASE, iSavepoint);
                        }
                        if (rc == StatusCode.SQLITE_OK)
                        {
                            rc = rc2;
                        }
                    }
                }
                db.nStatement--;
                p.iStatement = 0;

                /* If the statement transaction is being rolled back, also restore the 
        ** database handles deferred constraint counter to the value it had when 
        ** the statement transaction was opened.  */
                if (eOp == SavePoint.SAVEPOINT_ROLLBACK)
                {
                    db.nDeferredCons = p.nStmtDefCons;
                }
            }
            return rc;
        }

        /*
    ** If SQLite is compiled to support shared-cache mode and to be threadsafe,
    ** this routine obtains the mutex associated with each BtShared structure
    ** that may be accessed by the VM passed as an argument. In doing so it
    ** sets the BtShared.db member of each of the BtShared structures, ensuring
    ** that the correct busy-handler callback is invoked if required.
    **
    ** If SQLite is not threadsafe but does support shared-cache mode, then
    ** sqlite3BtreeEnterAll() is invoked to set the BtShared.db variables
    ** of all of BtShared structures accessible via the database handle
    ** associated with the VM. Of course only a subset of these structures
    ** will be accessed by the VM, and we could use Vdbe.btreeMask to figure
    ** that subset out, but there is no advantage to doing so.
    **
    ** If SQLite is not threadsafe and does not support shared-cache mode, this
    ** function is a no-op.
    */
#if !SQLITE_OMIT_SHARED_CACHE
static void sqlite3VdbeMutexArrayEnter(Vdbe p){
#if SQLITE_THREADSAFE
sqlite3BtreeMutexArrayEnter(&p->aMutex);
#else
sqlite3BtreeEnterAll(p.db);
#endif
}
#endif

        /*
** This function is called when a transaction opened by the database 
** handle associated with the VM passed as an argument is about to be 
** committed. If there are outstanding deferred foreign key constraint
** violations, return StatusCode.SQLITE_ERROR. Otherwise, StatusCode.SQLITE_OK.
**
** If there are outstanding FK violations and this function returns 
** StatusCode.SQLITE_ERROR, set the result of the VM to StatusCode.SQLITE_CONSTRAINT and write
** an error message to it. Then return StatusCode.SQLITE_ERROR.
*/
#if !SQLITE_OMIT_FOREIGN_KEY
        public static int VdbeCheckFk(Vdbe p, int deferred)
        {
            sqlite3 db = p.db;
            if ((deferred != 0 && db.nDeferredCons > 0) || (0 == deferred && p.nFkConstraint > 0))
            {
                p.rc = StatusCode.SQLITE_CONSTRAINT;
                p.errorAction = OnConstraintError.OE_Abort;
                Malloc.SetString(ref p.zErrMsg, db, "foreign key constraint failed");
                return StatusCode.SQLITE_ERROR;
            }
            return StatusCode.SQLITE_OK;
        }
#endif

        /*
** This routine is called the when a VDBE tries to halt.  If the VDBE
** has made changes and is in autocommit mode, then commit those
** changes.  If a rollback is needed, then do the rollback.
**
** This routine is the only way to move the state of a VM from
** SQLITE_MAGIC_RUN to SQLITE_MAGIC_HALT.  It is harmless to
** call this on a VM that is in the SQLITE_MAGIC_HALT state.
**
** Return an error code.  If the commit could not complete because of
** lock contention, return StatusCode.SQLITE_BUSY.  If StatusCode.SQLITE_BUSY is returned, it
** means the close did not happen and needs to be repeated.
*/

        public static int VdbeHalt(Vdbe p)
        {
            int rc; /* Used to store transient return codes */
            sqlite3 db = p.db;

            /* This function contains the logic that determines if a statement or
      ** transaction will be committed or rolled back as a result of the
      ** execution of this virtual machine.
      **
      ** If any of the following errors occur:
      **
      **     StatusCode.SQLITE_NOMEM
      **     StatusCode.SQLITE_IOERR
      **     StatusCode.SQLITE_FULL
      **     StatusCode.SQLITE_INTERRUPT
      **
      ** Then the internal cache might have been left in an inconsistent
      ** state.  We need to rollback the statement transaction, if there is
      ** one, or the complete transaction if there is no statement transaction.
      */

            //if ( p.db.mallocFailed != 0 )
            //{
            //  p.rc = StatusCode.SQLITE_NOMEM;
            //}
            closeAllCursors(p);
            if (p.magic != VdbeMagic.VDBE_MAGIC_RUN)
            {
                return StatusCode.SQLITE_OK;
            }
            checkActiveVdbeCnt(db);

            /* No commit or rollback needed if the program never started */
            if (p.pc >= 0)
            {
                int mrc; /* Primary error code from p.rc */
                int eStatementOp = 0;
                bool isSpecialError = false; /* Set to true if a 'special' error */

                /* Lock all btrees used by the statement */
                sqlite3VdbeMutexArrayEnter(p);
                /* Check for one of the special errors */
                mrc = p.rc & 0xff;
                Debug.Assert(p.rc != ExtendedResultCode.SQLITE_IOERR_BLOCKED); /* This error no longer exists */
                isSpecialError = mrc == StatusCode.SQLITE_NOMEM || mrc == StatusCode.SQLITE_IOERR
                                 || mrc == StatusCode.SQLITE_INTERRUPT || mrc == StatusCode.SQLITE_FULL;
                if (isSpecialError)
                {
                    /* If the query was read-only, we need do no rollback at all. Otherwise,
          ** proceed with the special handling.
          */
                    if (!p.readOnly || mrc != StatusCode.SQLITE_INTERRUPT)
                    {
                        if ((mrc == StatusCode.SQLITE_NOMEM || mrc == StatusCode.SQLITE_FULL) && p.usesStmtJournal)
                        {
                            eStatementOp = SavePoint.SAVEPOINT_ROLLBACK;
                        }
                        else
                        {
                            /* We are forced to roll back the active transaction. Before doing
              ** so, abort any other statements this handle currently has active.
              */
                            invalidateCursorsOnModifiedBtrees(db);
                            sqlite3RollbackAll(db);
                            sqlite3CloseSavepoints(db);
                            db.autoCommit = 1;
                        }
                    }
                }

                /* Check for immediate foreign key violations. */
                if (p.rc == StatusCode.SQLITE_OK)
                {
                    VdbeCheckFk(p, 0);
                }

                /* If the auto-commit flag is set and this is the only active writer
        ** VM, then we do either a commit or rollback of the current transaction.
        **
        ** Note: This block also runs if one of the special errors handled
        ** above has occurred.
        */
                if (!sqlite3VtabInSync(db)
                    && db.autoCommit != 0
                    && db.writeVdbeCnt == ((p.readOnly == false) ? 1 : 0)
                    )
                {
                    if (p.rc == StatusCode.SQLITE_OK || (p.errorAction == OnConstraintError.OE_Fail && !isSpecialError))
                    {
                        if (VdbeCheckFk(p, 1) != 0)
                        {
                            sqlite3BtreeMutexArrayLeave(p.aMutex);
                            return StatusCode.SQLITE_ERROR;
                        }
                        /* The auto-commit flag is true, the vdbe program was successful 
            ** or hit an 'OR FAIL' constraint and there are no deferred foreign
            ** key constraints to hold up the transaction. This means a commit 
            ** is required.  */
                        rc = vdbeCommit(db, p);
                        if (rc == StatusCode.SQLITE_BUSY)
                        {
                            sqlite3BtreeMutexArrayLeave(p.aMutex);
                            return StatusCode.SQLITE_BUSY;
                        }
                        else if (rc != StatusCode.SQLITE_OK)
                        {
                            p.rc = rc;
                            sqlite3RollbackAll(db);
                        }
                        else
                        {
                            db.nDeferredCons = 0;
                            Build.CommitInternalChanges(db);
                        }
                    }
                    else
                    {
                        sqlite3RollbackAll(db);
                    }
                    db.nStatement = 0;
                }
                else if (eStatementOp == 0)
                {
                    if (p.rc == StatusCode.SQLITE_OK || p.errorAction == OnConstraintError.OE_Fail)
                    {
                        eStatementOp = SavePoint.SAVEPOINT_RELEASE;
                    }
                    else if (p.errorAction == OnConstraintError.OE_Abort)
                    {
                        eStatementOp = SavePoint.SAVEPOINT_ROLLBACK;
                    }
                    else
                    {
                        invalidateCursorsOnModifiedBtrees(db);
                        sqlite3RollbackAll(db);
                        sqlite3CloseSavepoints(db);
                        db.autoCommit = 1;
                    }
                }

                /* If eStatementOp is non-zero, then a statement transaction needs to
        ** be committed or rolled back. Call VdbeAux.VdbeCloseStatement() to
        ** do so. If this operation returns an error, and the current statement
        ** error code is StatusCode.SQLITE_OK or StatusCode.SQLITE_CONSTRAINT, then promote the
        ** current statement error code.
        **
        ** Note that VdbeAux.VdbeCloseStatement() can only fail if eStatementOp
        ** is SavePoint.SAVEPOINT_ROLLBACK.  But if p->rc==StatusCode.SQLITE_OK then eStatementOp
        ** must be SavePoint.SAVEPOINT_RELEASE.  Hence the UnitTest.NEVER(p->rc==StatusCode.SQLITE_OK) in 
        ** the following code.
        */
                if (eStatementOp != 0)
                {
                    rc = VdbeCloseStatement(p, eStatementOp);
                    if (rc != 0 && (UnitTest.NEVER(p.rc == StatusCode.SQLITE_OK) || p.rc == StatusCode.SQLITE_CONSTRAINT))
                    {
                        p.rc = rc;
                        MemPool.DbFree(db, ref p.zErrMsg);
                        p.zErrMsg = null;
                    }
                }

                /* If this was an INSERT, UPDATE or DELETE and no statement transaction
        ** has been rolled back, update the database connection change-counter.
        */
                if (p.changeCntOn)
                {
                    if (eStatementOp != SavePoint.SAVEPOINT_ROLLBACK)
                    {
                        VdbeSetChanges(db, p.nChange);
                    }
                    else
                    {
                        VdbeSetChanges(db, 0);
                    }
                    p.nChange = 0;
                }

                /* Rollback or commit any schema changes that occurred. */
                if (p.rc != StatusCode.SQLITE_OK && (db.flags & Flag.SQLITE_InternChanges) != 0)
                {
                    Build.ResetInternalSchema(db, 0);
                    db.flags = (db.flags | Flag.SQLITE_InternChanges);
                }

                /* Release the locks */
                sqlite3BtreeMutexArrayLeave(p.aMutex);
            }

            /* We have successfully halted and closed the VM.  Record this fact. */
            if (p.pc >= 0)
            {
                db.activeVdbeCnt--;
                if (!p.readOnly)
                {
                    db.writeVdbeCnt--;
                }
                Debug.Assert(db.activeVdbeCnt >= db.writeVdbeCnt);
            }
            p.magic = VdbeMagic.VDBE_MAGIC_HALT;
            checkActiveVdbeCnt(db);
            //if ( p.db.mallocFailed != 0 )
            //{
            //  p.rc = StatusCode.SQLITE_NOMEM;
            //}
            /* If the auto-commit flag is set to true, then any locks that were held
      ** by connection db have now been released. Call sqlite3ConnectionUnlocked()
      ** to invoke any required unlock-notify callbacks.
      */
            if (db.autoCommit != 0)
            {
                sqlite3ConnectionUnlocked(db);
            }

            Debug.Assert(db.activeVdbeCnt > 0 || db.autoCommit == 0 || db.nStatement == 0);
            return StatusCode.SQLITE_OK;
        }


        /*
    ** Each VDBE holds the result of the most recent sqlite3_step() call
    ** in p.rc.  This routine sets that result back to StatusCode.SQLITE_OK.
    */

        public static void VdbeResetStepResult(Vdbe p)
        {
            p.rc = StatusCode.SQLITE_OK;
        }

        /*
    ** Clean up a VDBE after execution but do not delete the VDBE just yet.
    ** Write any error messages into pzErrMsg.  Return the result code.
    **
    ** After this routine is run, the VDBE should be ready to be executed
    ** again.
    **
    ** To look at it another way, this routine resets the state of the
    ** virtual machine from VdbeMagic.VDBE_MAGIC_RUN or VdbeMagic.VDBE_MAGIC_HALT back to
    ** VdbeMagic.VDBE_MAGIC_INIT.
    */

        public static int VdbeReset(Vdbe p)
        {
            sqlite3 db;
            db = p.db;

            /* If the VM did not run to completion or if it encountered an
      ** error, then it might not have been halted properly.  So halt
      ** it now.
      */
            VdbeHalt(p);

            /* If the VDBE has be run even partially, then transfer the error code
      ** and error message from the VDBE into the main database structure.  But
      ** if the VDBE has just been set to run but has not actually executed any
      ** instructions yet, leave the main database error information unchanged.
      */
            if (p.pc >= 0)
            {
                //if ( p.zErrMsg != 0 ) // Always exists under C#
                {
                    sqlite3BeginBenignMalloc();
                    sqlite3ValueSetStr(db.pErr, -1, p.zErrMsg == null ? "" : p.zErrMsg, Const.SQLITE_UTF8, Const.SQLITE_TRANSIENT);
                    sqlite3EndBenignMalloc();
                    db.errCode = p.rc;
                    MemPool.DbFree(db, ref p.zErrMsg);
                    p.zErrMsg = "";
                }
                //else if ( p.rc != 0 )
                //{
                //  Utility.Sqlite3Error( db, p.rc, 0 );
                //}
                //else
                //{
                //  Utility.Sqlite3Error( db, StatusCode.SQLITE_OK, 0 );
                //}
                if (p.runOnlyOnce != 0) p.expired = true;
            }
            else if (p.rc != 0 && p.expired)
            {
                /* The expired flag was set on the VDBE before the first call
        ** to sqlite3_step(). For consistency (since sqlite3_step() was
        ** called), set the database error in this case as well.
        */
                Utility.Sqlite3Error(db, p.rc, 0);
                sqlite3ValueSetStr(db.pErr, -1, p.zErrMsg, Const.SQLITE_UTF8, Const.SQLITE_TRANSIENT);
                MemPool.DbFree(db, ref p.zErrMsg);
                p.zErrMsg = "";
            }

            /* Reclaim all memory used by the VDBE
      */
            Cleanup(p);

            /* Save profiling information from this VDBE run.
      */
#if  VDBE_PROFILE && TODO
{
FILE *out = fopen("vdbe_profile.out", "a");
if( out ){
int i;
Custom.Fprintf(out, "---- ");
for(i=0; i<p.nOp; i++){
Custom.Fprintf(out, "%02x", p.aOp[i].opcode);
}
Custom.Fprintf(out, "\n");
for(i=0; i<p.nOp; i++){
Custom.Fprintf(out, "%6d %10lld %8lld ",
p.aOp[i].cnt,
p.aOp[i].cycles,
p.aOp[i].cnt>0 ? p.aOp[i].cycles/p.aOp[i].cnt : 0
);
VdbeAux.VdbePrintOp(out, i, p.aOp[i]);
}
fclose(out);
}
}
#endif
            p.magic = VdbeMagic.VDBE_MAGIC_INIT;
            return p.rc & db.errMask;
        }

        /*
    ** Clean up and delete a VDBE after execution.  Return an integer which is
    ** the result code.  Write any error message text into pzErrMsg.
    */

        public static int VdbeFinalize(Vdbe p)
        {
            int rc = StatusCode.SQLITE_OK;
            if (p.magic == VdbeMagic.VDBE_MAGIC_RUN || p.magic == VdbeMagic.VDBE_MAGIC_HALT)
            {
                rc = VdbeReset(p);
                Debug.Assert((rc & p.db.errMask) == rc);
            }
            VdbeDelete(ref p);
            return rc;
        }

        /*
    ** Call the destructor for each auxdata entry in pVdbeFunc for which
    ** the corresponding bit in mask is clear.  Auxdata entries beyond 31
    ** are always destroyed.  To destroy all auxdata entries, call this
    ** routine with mask==0.
    */

        public static void VdbeDeleteAuxData(VdbeFunc pVdbeFunc, int mask)
        {
            int i;
            for (i = 0; i < pVdbeFunc.nAux; i++)
            {
                AuxData pAux = pVdbeFunc.apAux[i];
                if ((i > 31 || (mask & (((uint)1) << i)) == 0 && pAux.pAux != null))
                {
                    if (pAux.xDelete != null)
                    {
                        pAux.xDelete(ref pAux.pAux);
                    }
                    pAux.pAux = null;
                }
            }
        }

        /// <summary>
        ///  Delete an entire VDBE.
        /// </summary>
        public static void VdbeDelete(ref Vdbe p)
        {
            sqlite3 db;
            if (UnitTest.NEVER(p == null)) return;
            Cleanup(p);
            db = p.db;
            if (p.pPrev != null)
            {
                p.pPrev.pNext = p.pNext;
            }
            else
            {
                Debug.Assert(db.pVdbe == p);
                db.pVdbe = p.pNext;
            }
            if (p.pNext != null)
            {
                p.pNext.pPrev = p.pPrev;
            }
            releaseMemArray(p.aVar, p.nVar);
            releaseMemArray(p.aColName, p.nResColumn * VdbeColnameType.COLNAME_N);
            vdbeFreeOpArray(db, ref p.aOp, p.nOp);
            MemPool.DbFree(db, ref p.aLabel);
            MemPool.DbFree(db, ref p.aColName);
            MemPool.DbFree(db, ref p.zSql);
            p.magic = VdbeMagic.VDBE_MAGIC_DEAD;
            MemPool.DbFree(db, ref p.pFree);
            p.db = null;
            if (p.aMem != null) releaseMemArray(p.aMem, p.aMem.Length);
            if (p.apArg != null) releaseMemArray(p.apArg, p.apArg.Length);
            MemPool.DbFree(db, ref p);
        }

        /*
    ** Make sure the cursor p is ready to read or write the row to which it
    ** was last positioned.  Return an error code if an OOM fault or I/O error
    ** prevents us from positioning the cursor to its correct position.
    **
    ** If a MoveTo operation is pending on the given cursor, then do that
    ** MoveTo now.  If no move is pending, check to see if the row has been
    ** deleted out from under the cursor and if it has, mark the row as
    ** a NULL row.
    **
    ** If the cursor is already pointing to the correct row and that row has
    ** not been deleted out from under the cursor, then this routine is a no-op.
    */

        public static int VdbeCursorMoveto(VdbeCursor p)
        {
            if (p.deferredMoveto)
            {
                int res = 0;
                int rc;
#if  SQLITE_TEST
    //extern int sqlite3_search_count;
#endif
                Debug.Assert(p.isTable);
                rc = BTreeHelper.BtreeMovetoUnpacked(p.pCursor, null, p.movetoTarget, 0, ref res);
                if (rc != 0) return rc;
                p.lastRowid = p.movetoTarget;
                p.rowidIsValid = UnitTest.ALWAYS(res == 0) ? true : false;
                if (UnitTest.NEVER(res < 0))
                {
                    rc = BTreeHelper.BtreeNext(p.pCursor, ref res);
                    if (rc != 0) return rc;
                }
#if  SQLITE_TEST
        sqlite3_search_count.iValue++;
#endif
                p.deferredMoveto = false;
                p.cacheStatus = Const.CACHE_STALE;
            }
            else if (UnitTest.ALWAYS(p.pCursor != null))
            {
                int hasMoved = 0;
                int rc = sqlite3BtreeCursorHasMoved(p.pCursor, ref hasMoved);
                if (rc != 0) return rc;
                if (hasMoved != 0)
                {
                    p.cacheStatus = Const.CACHE_STALE;
                    p.nullRow = true;
                }
            }
            return StatusCode.SQLITE_OK;
        }

        /*
    ** The following functions:
    **
    ** VdbeAux.VdbeSerialType()
    ** VdbeAux.VdbeSerialTypeLen()
    ** sqlite3VdbeSerialLen()
    ** sqlite3VdbeSerialPut()
    ** VdbeAux.VdbeSerialGet()
    **
    ** encapsulate the code that serializes values for storage in SQLite
    ** data and index records. Each serialized value consists of a
    ** 'serial-type' and a blob of data. The serial type is an 8-byte unsigned
    ** integer, stored as a varint.
    **
    ** In an SQLite index record, the serial type is stored directly before
    ** the blob of data that it corresponds to. In a table record, all serial
    ** types are stored at the start of the record, and the blobs of data at
    ** the end. Hence these functions allow the caller to handle the
    ** serial-type and data blob seperately.
    **
    ** The following table describes the various storage classes for data:
    **
    **   serial type        bytes of data      type
    **   --------------     ---------------    ---------------
    **      0                     0            NULL
    **      1                     1            signed integer
    **      2                     2            signed integer
    **      3                     3            signed integer
    **      4                     4            signed integer
    **      5                     6            signed integer
    **      6                     8            signed integer
    **      7                     8            IEEE float
    **      8                     0            Integer constant 0
    **      9                     0            Integer constant 1
    **     10,11                               reserved for expansion
    **    N>=12 and even       (N-12)/2        BLOB
    **    N>=13 and odd        (N-13)/2        text
    **
    ** The 8 and 9 types were added in 3.3.0, file format 4.  Prior versions
    ** of SQLite will not understand those serial types.
    */

        /*
    ** Return the serial-type for the value stored in pMem.
    */

        public static uint VdbeSerialType(Mem pMem, int file_format)
        {
            int flags = pMem.flags;
            int n;

            if ((flags & MEMFlag.MEM_Null) != 0)
            {
                return 0;
            }
            if ((flags & MEMFlag.MEM_Int) != 0)
            {
                /* Figure out whether to use 1, 2, 4, 6 or 8 bytes. */
                const long MAX_6BYTE = ((((long)0x00008000) << 32) - 1);
                long i = pMem.u.i;
                ulong u;
                if (file_format >= 4 && (i & 1) == i)
                {
                    return 8 + (uint)i;
                }
                u = (ulong)(i < 0 ? -i : i);
                if (u <= 127) return 1;
                if (u <= 32767) return 2;
                if (u <= 8388607) return 3;
                if (u <= 2147483647) return 4;
                if (u <= MAX_6BYTE) return 5;
                return 6;
            }
            if ((flags & MEMFlag.MEM_Real) != 0)
            {
                return 7;
            }
            Debug.Assert( /* pMem.db.mallocFailed != 0 || */ (flags & (MEMFlag.MEM_Str | MEMFlag.MEM_Blob)) != 0);
            n = pMem.n;
            if ((flags & MEMTermFlag.MEM_Zero) != 0)
            {
                n += pMem.u.nZero;
            }
            else if ((flags & MEMFlag.MEM_Blob) != 0)
            {
                n = pMem.zBLOB != null ? pMem.zBLOB.Length : pMem.z != null ? pMem.z.Length : 0;
            }
            else
            {
                if (pMem.z != null)
                    n = Encoding.UTF8.GetByteCount(pMem.n < pMem.z.Length ? pMem.z.Substring(0, pMem.n) : pMem.z);
                else n = pMem.zBLOB.Length;
                pMem.n = n;
            }

            Debug.Assert(n >= 0);
            return (uint)((n * 2) + 12 + (((flags & MEMFlag.MEM_Str) != 0) ? 1 : 0));
        }

        /*
    ** Return the length of the data corresponding to the supplied serial-type.
    */

        public static uint VdbeSerialTypeLen(uint serial_type)
        {
            if (serial_type >= 12)
            {
                return ((serial_type - 12) / 2);
            }
            else
            {
                var aSize = new uint[] { 0, 1, 2, 3, 4, 6, 8, 8, 0, 0, 0, 0 };
                return aSize[serial_type];
            }
        }

        /*
    ** If we are on an architecture with mixed-endian floating
    ** points (ex: ARM7) then swap the lower 4 bytes with the
    ** upper 4 bytes.  Return the result.
    **
    ** For most architectures, this is a no-op.
    **
    ** (later):  It is reported to me that the mixed-endian problem
    ** on ARM7 is an issue with GCC, not with the ARM7 chip.  It seems
    ** that early versions of GCC stored the two words of a 64-bit
    ** float in the wrong order.  And that error has been propagated
    ** ever since.  The blame is not necessarily with GCC, though.
    ** GCC might have just copying the problem from a prior compiler.
    ** I am also told that newer versions of GCC that follow a different
    ** ABI get the byte order right.
    **
    ** Developers using SQLite on an ARM7 should compile and run their
    ** application using -DSQLITE_DEBUG=1 at least once.  With DEBUG
    ** enabled, some Debug.Asserts below will ensure that the byte order of
    ** floating point values is correct.
    **
    ** (2007-08-30)  Frank van Vugt has studied this problem closely
    ** and has send his findings to the SQLite developers.  Frank
    ** writes that some Linux kernels offer floating point hardware
    ** emulation that uses only 32-bit mantissas instead of a full
    ** 48-bits as required by the IEEE standard.  (This is the
    ** CONFIG_FPE_FASTFPE option.)  On such systems, floating point
    ** byte swapping becomes very complicated.  To avoid problems,
    ** the necessary byte swapping is carried out using a 64-bit integer
    ** rather than a 64-bit float.  Frank assures us that the code here
    ** works for him.  We, the developers, have no way to independently
    ** verify this, but Frank seems to know what he is talking about
    ** so we trust him.
    */
#if  SQLITE_MIXED_ENDIAN_64BIT_FLOAT
//static ulong floatSwap(ulong in){
//  union {
//    ulong r;
//    uint i[2];
//  } u;
//  uint t;

//  u.r = in;
//  t = u.i[0];
//  u.i[0] = u.i[1];
//  u.i[1] = t;
//  return u.r;
//}
//# define swapMixedEndianFloat(X)  X = floatSwap(X)
#else
        //# define swapMixedEndianFloat(X)
#endif

        /*
** Write the serialized data blob for the value stored in pMem into
** buf. It is assumed that the caller has allocated sufficient space.
** Return the number of bytes written.
**
** nBuf is the amount of space left in buf[].  nBuf must always be
** large enough to hold the entire field.  Except, if the field is
** a blob with a zero-filled tail, then buf[] might be just the right
** size to hold everything except for the zero-filled tail.  If buf[]
** is only big enough to hold the non-zero prefix, then only write that
** prefix into buf[].  But if buf[] is large enough to hold both the
** prefix and the tail then write the prefix and set the tail to all
** zeros.
**
** Return the number of bytes actually written into buf[].  The number
** of bytes in the zero-filled tail is included in the return value only
** if those bytes were zeroed in buf[].
*/

        public static uint VdbeSerialPut(byte[] buf, int offset, int nBuf, Mem pMem, int file_format)
        {
            uint serial_type = VdbeSerialType(pMem, file_format);
            uint len;

            /* Integer and Real */
            if (serial_type <= 7 && serial_type > 0)
            {
                ulong v;
                uint i;
                if (serial_type == 7)
                {
                    //Debug.Assert( sizeof( v) == sizeof(pMem.r));
                    v = (ulong)BitConverter.DoubleToInt64Bits(pMem.r); // memcpy( &v, pMem.r, v ).Length;
#if  SQLITE_MIXED_ENDIAN_64BIT_FLOAT
swapMixedEndianFloat( v );
#endif
                }
                else
                {
                    v = (ulong)pMem.u.i;
                }
                len = i = VdbeSerialTypeLen(serial_type);
                Debug.Assert(len <= (uint)nBuf);
                while (i-- != 0)
                {
                    buf[offset + i] = (byte)(v & 0xFF);
                    v >>= 8;
                }
                return len;
            }

            /* String or blob */
            if (serial_type >= 12)
            {
                Debug.Assert(pMem.n + ((pMem.flags & MEMTermFlag.MEM_Zero) != 0 ? pMem.u.nZero : 0) ==
                             (int)VdbeSerialTypeLen(serial_type));
                Debug.Assert(pMem.n <= nBuf);
                if ((len = (uint)pMem.n) != 0)
                    if (pMem.zBLOB == null && String.IsNullOrEmpty(pMem.z))
                    {
                    }
                    else if ((pMem.flags & MEMFlag.MEM_Blob) != 0 || pMem.z == null)
                        Buffer.BlockCopy(pMem.zBLOB, 0, buf, offset, (int)len); //memcpy( buf, pMem.z, len );
                    else
                        Buffer.BlockCopy(Encoding.UTF8.GetBytes(pMem.z), 0, buf, offset, (int)len);
                //memcpy( buf, pMem.z, len );
                if ((pMem.flags & MEMTermFlag.MEM_Zero) != 0)
                {
                    len += (uint)pMem.u.nZero;
                    Debug.Assert(nBuf >= 0);
                    if (len > (uint)nBuf)
                    {
                        len = (uint)nBuf;
                    }
                    Array.Clear(buf, offset + pMem.n, (int)(len - pMem.n)); // memset( &buf[pMem.n], 0, len - pMem.n );
                }
                return len;
            }

            /* NULL or constants 0 or 1 */
            return 0;
        }

        /*
    ** Deserialize the data blob pointed to by buf as serial type serial_type
    ** and store the result in pMem.  Return the number of bytes read.
    */

        public static uint VdbeSerialGet(
            byte[] buf, /* Buffer to deserialize from */
            int offset, /* Offset into Buffer */
            uint serial_type, /* Serial type to deserialize */
            Mem pMem /* Memory cell to write value into */
            )
        {
            switch (serial_type)
            {
                case 10: /* Reserved for future use */
                case 11: /* Reserved for future use */
                case 0:
                    {
                        /* NULL */
                        pMem.flags = MEMFlag.MEM_Null;
                        pMem.n = 0;
                        pMem.z = null;
                        pMem.zBLOB = null;
                        break;
                    }
                case 1:
                    {
                        /* 1-byte signed integer */
                        pMem.u.i = (sbyte)buf[offset + 0];
                        pMem.flags = MEMFlag.MEM_Int;
                        return 1;
                    }
                case 2:
                    {
                        /* 2-byte signed integer */
                        pMem.u.i = ((((sbyte)buf[offset + 0]) << 8) | buf[offset + 1]);
                        pMem.flags = MEMFlag.MEM_Int;
                        return 2;
                    }
                case 3:
                    {
                        /* 3-byte signed integer */
                        pMem.u.i = ((((sbyte)buf[offset + 0]) << 16) | (buf[offset + 1] << 8) | buf[offset + 2]);
                        pMem.flags = MEMFlag.MEM_Int;
                        return 3;
                    }
                case 4:
                    {
                        /* 4-byte signed integer */
                        pMem.u.i = (((sbyte)buf[offset + 0] << 24) | (buf[offset + 1] << 16) | (buf[offset + 2] << 8) |
                                    buf[offset + 3]);
                        pMem.flags = MEMFlag.MEM_Int;
                        return 4;
                    }
                case 5:
                    {
                        /* 6-byte signed integer */
                        var x = (ulong)((((sbyte)buf[offset + 0]) << 8) | buf[offset + 1]);
                        var y =
                            (uint)
                            ((buf[offset + 2] << 24) | (buf[offset + 3] << 16) | (buf[offset + 4] << 8) |
                             buf[offset + 5]);
                        x = (x << 32) | y;
                        pMem.u.i = (long)x;
                        pMem.flags = MEMFlag.MEM_Int;
                        return 6;
                    }
                case 6: /* 8-byte signed integer */
                case 7:
                    {
                        /* IEEE floating point */
                        ulong x;
                        uint y;
#if !NDEBUG && !SQLITE_OMIT_FLOATING_POINT
    /* Verify that integers and floating point values use the same
** byte order.  Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
** defined that 64-bit floating point values really are mixed
** endian.
*/
            const ulong t1 = ( (ulong)0x3ff00000 ) << 32;
            const double r1 = 1.0;
            ulong t2 = t1;
#if  SQLITE_MIXED_ENDIAN_64BIT_FLOAT
swapMixedEndianFloat(t2);
#endif
            Debug.Assert( sizeof( double ) == sizeof( ulong ) && Custom.Memcmp( BitConverter.GetBytes( r1 ), BitConverter.GetBytes( t2 ), sizeof( double ) ) == 0 );//Debug.Assert( sizeof(r1)==sizeof(t2) && Custom.Memcmp(&r1, t2, sizeof(r1))==0 );
#endif

                        x =
                            (ulong)
                            ((buf[offset + 0] << 24) | (buf[offset + 1] << 16) | (buf[offset + 2] << 8) |
                             buf[offset + 3]);
                        y =
                            (uint)
                            ((buf[offset + 4] << 24) | (buf[offset + 5] << 16) | (buf[offset + 6] << 8) |
                             buf[offset + 7]);
                        x = (x << 32) | y;
                        if (serial_type == 6)
                        {
                            pMem.u.i = (long)x;
                            pMem.flags = MEMFlag.MEM_Int;
                        }
                        else
                        {
                            Debug.Assert(sizeof(long) == 8 && sizeof(double) == 8);
#if  SQLITE_MIXED_ENDIAN_64BIT_FLOAT
swapMixedEndianFloat(x);
#endif
                            pMem.r = BitConverter.Int64BitsToDouble((long)x); // memcpy(pMem.r, x, sizeof(x))
                            pMem.flags = (ushort)(Utility.Sqlite3IsNaN(pMem.r) ? MEMFlag.MEM_Null : MEMFlag.MEM_Real);
                        }
                        return 8;
                    }
                case 8: /* Integer 0 */
                case 9:
                    {
                        /* Integer 1 */
                        pMem.u.i = serial_type - 8;
                        pMem.flags = MEMFlag.MEM_Int;
                        return 0;
                    }
                default:
                    {
                        uint len = (serial_type - 12) / 2;
                        pMem.n = (int)len;
                        pMem.xDel = null;
                        if ((serial_type & 0x01) != 0)
                        {
                            pMem.flags = MEMFlag.MEM_Str | MEMTermFlag.MEM_Ephem;
                            pMem.z = Encoding.UTF8.GetString(buf, offset, (int)len); //memcpy( buf, pMem.z, len );
                            pMem.n = pMem.z.Length;
                            pMem.zBLOB = null;
                        }
                        else
                        {
                            pMem.z = null;
                            pMem.zBLOB = Malloc.sqlite3Malloc((int)len);
                            pMem.flags = MEMFlag.MEM_Blob | MEMTermFlag.MEM_Ephem;
                            Buffer.BlockCopy(buf, offset, pMem.zBLOB, 0, (int)len); //memcpy( buf, pMem.z, len );
                        }
                        return len;
                    }
            }
            return 0;
        }

        public static int VdbeSerialGet(
            byte[] buf, /* Buffer to deserialize from */
            uint serial_type, /* Serial type to deserialize */
            Mem pMem /* Memory cell to write value into */
            )
        {
            switch (serial_type)
            {
                case 10: /* Reserved for future use */
                case 11: /* Reserved for future use */
                case 0:
                    {
                        /* NULL */
                        pMem.flags = MEMFlag.MEM_Null;
                        break;
                    }
                case 1:
                    {
                        /* 1-byte signed integer */
                        pMem.u.i = (sbyte)buf[0];
                        pMem.flags = MEMFlag.MEM_Int;
                        return 1;
                    }
                case 2:
                    {
                        /* 2-byte signed integer */
                        pMem.u.i = (((buf[0]) << 8) | buf[1]);
                        pMem.flags = MEMFlag.MEM_Int;
                        return 2;
                    }
                case 3:
                    {
                        /* 3-byte signed integer */
                        pMem.u.i = (((buf[0]) << 16) | (buf[1] << 8) | buf[2]);
                        pMem.flags = MEMFlag.MEM_Int;
                        return 3;
                    }
                case 4:
                    {
                        /* 4-byte signed integer */
                        pMem.u.i = ((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
                        pMem.flags = MEMFlag.MEM_Int;
                        return 4;
                    }
                case 5:
                    {
                        /* 6-byte signed integer */
                        var x = (ulong)(((buf[0]) << 8) | buf[1]);
                        var y = (uint)((buf[2] << 24) | (buf[3] << 16) | (buf[4] << 8) | buf[5]);
                        x = (x << 32) | y;
                        pMem.u.i = (long)x;
                        pMem.flags = MEMFlag.MEM_Int;
                        return 6;
                    }
                case 6: /* 8-byte signed integer */
                case 7:
                    {
                        /* IEEE floating point */
                        ulong x;
                        uint y;
#if !NDEBUG && !SQLITE_OMIT_FLOATING_POINT
    /* Verify that integers and floating point values use the same
** byte order.  Or, that if SQLITE_MIXED_ENDIAN_64BIT_FLOAT is
** defined that 64-bit floating point values really are mixed
** endian.
*/
            const ulong t1 = ( (ulong)0x3ff00000 ) << 32;
            const double r1 = 1.0;
            ulong t2 = t1;
#if  SQLITE_MIXED_ENDIAN_64BIT_FLOAT
swapMixedEndianFloat(t2);
#endif
            Debug.Assert( sizeof( double ) == sizeof( ulong ) && Custom.Memcmp( BitConverter.GetBytes( r1 ), BitConverter.GetBytes( t2 ), sizeof( double ) ) == 0 );//Debug.Assert( sizeof(r1)==sizeof(t2) && Custom.Memcmp(&r1, t2, sizeof(r1))==0 );
#endif

                        x = (ulong)((buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]);
                        y = (uint)((buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7]);
                        x = (x << 32) | y;
                        if (serial_type == 6)
                        {
                            pMem.u.i = (long)x;
                            pMem.flags = MEMFlag.MEM_Int;
                        }
                        else
                        {
                            Debug.Assert(sizeof(long) == 8 && sizeof(double) == 8);
#if  SQLITE_MIXED_ENDIAN_64BIT_FLOAT
swapMixedEndianFloat(x);
#endif
                            pMem.r = BitConverter.Int64BitsToDouble((long)x); // memcpy(pMem.r, x, sizeof(x))
                            pMem.flags = MEMFlag.MEM_Real;
                        }
                        return 8;
                    }
                case 8: /* Integer 0 */
                case 9:
                    {
                        /* Integer 1 */
                        pMem.u.i = serial_type - 8;
                        pMem.flags = MEMFlag.MEM_Int;
                        return 0;
                    }
                default:
                    {
                        var len = (int)((serial_type - 12) / 2);
                        pMem.xDel = null;
                        if ((serial_type & 0x01) != 0)
                        {
                            pMem.flags = MEMFlag.MEM_Str | MEMTermFlag.MEM_Ephem;
                            pMem.z = Encoding.UTF8.GetString(buf, 0, len); //memcpy( buf, pMem.z, len );
                            pMem.n = pMem.z.Length; // len;
                            pMem.zBLOB = null;
                        }
                        else
                        {
                            pMem.flags = MEMFlag.MEM_Blob | MEMTermFlag.MEM_Ephem;
                            pMem.zBLOB = Malloc.sqlite3Malloc(len);
                            buf.CopyTo(pMem.zBLOB, 0);
                            pMem.n = len; // len;
                            pMem.z = null;
                        }
                        return len;
                    }
            }
            return 0;
        }

        /*
    ** Given the nKey-byte encoding of a record in pKey[], parse the
    ** record into a UnpackedRecord structure.  Return a pointer to
    ** that structure.
    **
    ** The calling function might provide szSpace bytes of memory
    ** space at pSpace.  This space can be used to hold the returned
    ** VDbeParsedRecord structure if it is large enough.  If it is
    ** not big enough, space is obtained from Malloc.sqlite3Malloc().
    **
    ** The returned structure should be closed by a call to
    ** VdbeAux.VdbeDeleteUnpackedRecord().
    */

        public static UnpackedRecord VdbeRecordUnpack(
            KeyInfo pKeyInfo, /* Information about the record format */
            int nKey, /* Size of the binary record */
            byte[] pKey, /* The binary record */
            UnpackedRecord pSpace, //  char *pSpace,          /* Unaligned space available to hold the object */
            int szSpace /* Size of pSpace[] in bytes */
            )
        {
            byte[] aKey = pKey;
            UnpackedRecord p; /* The unpacked record that we will return */
            int nByte; /* Memory space needed to hold p, in bytes */
            int d;
            uint idx;
            int u; /* Unsigned loop counter */
            int szHdr = 0;
            Mem pMem;
            int nOff; /* Increase pSpace by this much to 8-byte align it */

            /*
      ** We want to shift the pointer pSpace up such that it is 8-byte aligned.
      ** Thus, we need to calculate a value, nOff, between 0 and 7, to shift
      ** it by.  If pSpace is already 8-byte aligned, nOff should be zero.
      */
            //nOff = ( 8 - ( SQLITE_PTR_TO_INT( pSpace ) & 7 ) ) & 7;
            //pSpace += nOff;
            //szSpace -= nOff;
            //nByte = Utility.ROUND8( sizeof( UnpackedRecord ) ) + sizeof( Mem ) * ( pKeyInfo->nField + 1 );
            //if ( nByte > szSpace)
            //{
            //  p = new UnpackedRecord();//sqlite3DbMallocRaw(pKeyInfo.db, nByte);
            //  if ( p == null ) return null;
            //  p.flags = UnpackedRecordFlag.UNPACKED_NEED_FREE | UnpackedRecordFlag.UNPACKED_NEED_DESTROY;
            //}
            //else
            {
                p = pSpace; //(UnpackedRecord*)pSpace;
                p.flags = UnpackedRecordFlag.UNPACKED_NEED_DESTROY;
            }
            p.pKeyInfo = pKeyInfo;
            p.nField = (ushort)(pKeyInfo.nField + 1);
            //p->aMem = pMem = (Mem*)&( (char*)p )[Utility.ROUND8( sizeof( UnpackedRecord ) )];
            //assert( EIGHT_BYTE_ALIGNMENT( pMem ) );
            p.aMem = new Mem[p.nField + 1];
            idx = (uint)Utility.GetVarint32(aKey, 0, ref szHdr); // GetVarint( aKey, szHdr );
            d = szHdr;
            u = 0;
            while (idx < szHdr && u < p.nField && d <= nKey)
            {
                p.aMem[u] = Malloc.sqlite3Malloc(p.aMem[u]);
                pMem = p.aMem[u];
                uint serial_type = 0;

                idx += (uint)Utility.GetVarint32(aKey, idx, ref serial_type); // GetVarint( aKey + idx, serial_type );
                pMem.enc = pKeyInfo.enc;
                pMem.db = pKeyInfo.db;
                pMem.flags = 0;
                //pMem.zMalloc = null;
                d += (int)VdbeSerialGet(aKey, d, serial_type, pMem);
                //pMem++;
                u++;
            }
            Debug.Assert(u <= pKeyInfo.nField + 1);
            p.nField = (ushort)u;
            return p; // (void*)p;
        }

        /*
    ** This routine destroys a UnpackedRecord object.
    */

        public static void VdbeDeleteUnpackedRecord(UnpackedRecord p)
        {
            int i;
            Mem pMem;
            Debug.Assert(p != null);
            Debug.Assert((p.flags & UnpackedRecordFlag.UNPACKED_NEED_DESTROY) != 0);
            //for ( i = 0, pMem = p->aMem ; i < p->nField ; i++, pMem++ )
            //{
            //  /* The unpacked record is always constructed by the
            //  ** sqlite3VdbeUnpackRecord() function above, which makes all
            //  ** strings and blobs static.  And none of the elements are
            //  ** ever transformed, so there is never anything to delete.
            //  */
            //  if ( UnitTest.NEVER( pMem->zMalloc ) ) sqlite3VdbeMemRelease( pMem );
            //}
            if ((p.flags & UnpackedRecordFlag.UNPACKED_NEED_FREE) != 0)
            {
                MemPool.DbFree(p.pKeyInfo.db, ref p.aMem);
                p = null;
            }
        }

        /*
    ** This function compares the two table rows or index records
    ** specified by {nKey1, pKey1} and pPKey2.  It returns a negative, zero
    ** or positive integer if key1 is less than, equal to or
    ** greater than key2.  The {nKey1, pKey1} key must be a blob
    ** created by th OPCode.OP_MakeRecord opcode of the VDBE.  The pPKey2
    ** key must be a parsed key such as obtained from
    ** sqlite3VdbeParseRecord.
    **
    ** Key1 and Key2 do not have to contain the same number of fields.
    ** The key with fewer fields is usually compares less than the
    ** longer key.  However if the UnpackedRecordFlag.UNPACKED_INCRKEY flags in pPKey2 is set
    ** and the common prefixes are equal, then key1 is less than key2.
    ** Or if the UNPACKED_MATCH_PREFIX flag is set and the prefixes are
    ** equal, then the keys are considered to be equal and
    ** the parts beyond the common prefix are ignored.
    **
    ** If the UnpackedRecordFlag.UNPACKED_IGNORE_ROWID flag is set, then the last byte of
    ** the header of pKey1 is ignored.  It is assumed that pKey1 is
    ** an index key, and thus ends with a rowid value.  The last byte
    ** of the header will therefore be the serial type of the rowid:
    ** one of 1, 2, 3, 4, 5, 6, 8, or 9 - the integer serial types.
    ** The serial type of the final rowid will always be a single byte.
    ** By ignoring this last byte of the header, we force the comparison
    ** to ignore the rowid at the end of key1.
    */

        private static readonly Mem mem1 = new Mem();
        // ALTERNATE FORM for C#
        public static int VdbeRecordCompare(
            int nKey1, byte[] pKey1, /* Left key */
            UnpackedRecord pPKey2 /* Right key */
            )
        {
            return VdbeRecordCompare(nKey1, pKey1, 0, pPKey2);
        }

        public static int VdbeRecordCompare(
            int nKey1, byte[] pKey1, /* Left key */
            int offset,
            UnpackedRecord pPKey2 /* Right key */
            )
        {
            int d1; /* Offset into aKey[] of next data element */
            uint idx1; /* Offset into aKey[] of next header element */
            uint szHdr1; /* Number of bytes in header */
            int i = 0;
            int nField;
            int rc = 0;

            //byte[] aKey1 = new byte[pKey1.Length - offset];
            //Buffer.BlockCopy( pKey1, offset, aKey1, 0, aKey1.Length );
            KeyInfo pKeyInfo;

            pKeyInfo = pPKey2.pKeyInfo;
            mem1.enc = pKeyInfo.enc;
            mem1.db = pKeyInfo.db;
            /* mem1.flags = 0;  // Will be initialized by VdbeAux.VdbeSerialGet() */
            //  VVA_ONLY( mem1.zMalloc = 0; ) /* Only needed by assert() statements */

            /* Compilers may complain that mem1.u.i is potentially uninitialized.
      ** We could initialize it, as shown here, to silence those complaints.
      ** But in fact, mem1.u.i will never actually be used initialized, and doing 
      ** the unnecessary initialization has a measurable negative performance
      ** impact, since this routine is a very high runner.  And so, we choose
      ** to ignore the compiler warnings and leave this variable uninitialized.
      */
            /*  mem1.u.i = 0;  // not needed, here to silence compiler warning */

            idx1 = (uint)((szHdr1 = pKey1[offset]) <= 0x7f ? 1 : Utility.GetVarint32(pKey1, offset, ref szHdr1));
            // GetVarint( aKey1, szHdr1 );
            d1 = (int)szHdr1;
            if ((pPKey2.flags & UnpackedRecordFlag.UNPACKED_IGNORE_ROWID) != 0)
            {
                szHdr1--;
            }
            nField = pKeyInfo.nField;
            while (idx1 < szHdr1 && i < pPKey2.nField)
            {
                uint serial_type1;

                /* Read the serial types for the next element in each key. */
                idx1 +=
                    (uint)
                    ((serial_type1 = pKey1[offset + idx1]) <= 0x7f
                         ? 1
                         : Utility.GetVarint32(pKey1, (uint)(offset + idx1), ref serial_type1));
                //GetVarint( aKey1 + idx1, serial_type1 );
                if (d1 >= nKey1 && VdbeSerialTypeLen(serial_type1) > 0) break;

                /* Extract the values to be compared.
        */
                d1 += (int)VdbeSerialGet(pKey1, offset + d1, serial_type1, mem1);
                //VdbeAux.VdbeSerialGet( aKey1, d1, serial_type1, mem1 );

                /* Do the comparison
        */
                rc = sqlite3MemCompare(mem1, pPKey2.aMem[i], i < nField ? pKeyInfo.aColl[i] : null);
                if (rc != 0)
                {
                    //Debug.Assert( mem1.zMalloc==null );  /* See comment below */

                    /* Invert the result if we are using DESC sort order. */
                    if (pKeyInfo.aSortOrder != null && i < nField && pKeyInfo.aSortOrder[i] != 0)
                    {
                        rc = -rc;
                    }

                    /* If the PREFIX_SEARCH flag is set and all fields except the final
          ** rowid field were equal, then clear the PREFIX_SEARCH flag and set
          ** pPKey2->rowid to the value of the rowid field in (pKey1, nKey1).
          ** This is used by the OPCode.OP_IsUnique opcode.
          */
                    if ((pPKey2.flags & UnpackedRecordFlag.UNPACKED_PREFIX_SEARCH) != 0 && i == (pPKey2.nField - 1))
                    {
                        Debug.Assert(idx1 == szHdr1 && rc != 0);
                        Debug.Assert((mem1.flags & MEMFlag.MEM_Int) != 0);
                        pPKey2.flags = (ushort)(pPKey2.flags & ~UnpackedRecordFlag.UNPACKED_PREFIX_SEARCH);
                        pPKey2.rowid = mem1.u.i;
                    }

                    return rc;
                }
                i++;
            }

            /* No memory allocation is ever used on mem1.  Prove this using
      ** the following assert().  If the assert() fails, it indicates a
      ** memory leak and a need to call sqlite3VdbeMemRelease(&mem1).
      */
            //Debug.Assert( mem1.zMalloc==null );

            /* rc==0 here means that one of the keys ran out of fields and
      ** all the fields up to that point were equal. If the UnpackedRecordFlag.UNPACKED_INCRKEY
      ** flag is set, then break the tie by treating key2 as larger.
      ** If the UPACKED_PREFIX_MATCH flag is set, then keys with common prefixes
      ** are considered to be equal.  Otherwise, the longer key is the
      ** larger.  As it happens, the pPKey2 will always be the longer
      ** if there is a difference.
      */
            Debug.Assert(rc == 0);
            if ((pPKey2.flags & UnpackedRecordFlag.UNPACKED_INCRKEY) != 0)
            {
                rc = -1;
            }
            else if ((pPKey2.flags & UnpackedRecordFlag.UNPACKED_PREFIX_MATCH) != 0)
            {
                /* Leave rc==0 */
            }
            else if (idx1 < szHdr1)
            {
                rc = 1;
            }
            return rc;
        }

        /*
    ** pCur points at an index entry created using the OPCode.OP_MakeRecord opcode.
    ** Read the rowid (the last field in the record) and store it in *rowid.
    ** Return StatusCode.SQLITE_OK if everything works, or an error code otherwise.
    **
    ** pCur might be pointing to text obtained from a corrupt database file.
    ** So the content cannot be trusted.  Do appropriate checks on the content.
    */

        public static int VdbeIdxRowid(sqlite3 db, BtCursor pCur, ref long rowid)
        {
            long nCellKey = 0;
            int rc;
            uint szHdr = 0; /* Size of the header */
            uint typeRowid = 0; /* Serial type of the rowid */
            uint lenRowid; /* Size of the rowid */
            Mem m = null;
            Mem v = null;
            v = Malloc.sqlite3Malloc(v);
            Helper.UNUSED_PARAMETER(db);

            /* Get the size of the index entry.  Only indices entries of less
      ** than 2GiB are support - anything large must be database corruption.
      ** Any corruption is detected in sqlite3BtreeParseCellPtr(), though, so
      ** this code can safely assume that nCellKey is 32-bits  
      */
            Debug.Assert(BTreeHelper.BtreeCursorIsValid(pCur));
            rc = BTreeHelper.BtreeKeySize(pCur, ref nCellKey);
            Debug.Assert(rc == StatusCode.SQLITE_OK); /* pCur is always valid so KeySize cannot fail */
            Debug.Assert(((uint)nCellKey & Const.SQLITE_MAX_U32) == (ulong)nCellKey);

            /* Read in the complete content of the index entry */
            m = Malloc.sqlite3Malloc(m);
            // memset(&m, 0, sizeof(m));
            rc = sqlite3VdbeMemFromBtree(pCur, 0, (int)nCellKey, true, m);
            if (rc != 0)
            {
                return rc;
            }

            /* The index entry must begin with a header size */
            Utility.GetVarint32(m.zBLOB, 0, ref szHdr);
            UnitTest.TestCase(szHdr == 3);
            UnitTest.TestCase(szHdr == m.n);
            if (Utility.Unlikely(szHdr < 3 || (int)szHdr > m.n))
            {
                goto idx_rowid_corruption;
            }

            /* The last field of the index should be an integer - the ROWID.
      ** Verify that the last entry really is an integer. */
            Utility.GetVarint32(m.zBLOB, szHdr - 1, ref typeRowid);
            UnitTest.TestCase(typeRowid == 1);
            UnitTest.TestCase(typeRowid == 2);
            UnitTest.TestCase(typeRowid == 3);
            UnitTest.TestCase(typeRowid == 4);
            UnitTest.TestCase(typeRowid == 5);
            UnitTest.TestCase(typeRowid == 6);
            UnitTest.TestCase(typeRowid == 8);
            UnitTest.TestCase(typeRowid == 9);
            if (Utility.Unlikely(typeRowid < 1 || typeRowid > 9 || typeRowid == 7))
            {
                goto idx_rowid_corruption;
            }
            lenRowid = VdbeSerialTypeLen(typeRowid);
            UnitTest.TestCase((uint)m.n == szHdr + lenRowid);
            if (Utility.Unlikely((uint)m.n < szHdr + lenRowid))
            {
                goto idx_rowid_corruption;
            }

            /* Fetch the integer off the end of the index record */
            VdbeSerialGet(m.zBLOB, (int)(m.n - lenRowid), typeRowid, v);
            rowid = v.u.i;
            sqlite3VdbeMemRelease(m);
            return StatusCode.SQLITE_OK;

            /* Jump here if database corruption is detected after m has been
      ** allocated.  Free the m object and return StatusCode.SQLITE_CORRUPT. */
        idx_rowid_corruption:
            //UnitTest.TestCase( m.zMalloc != 0 );
            sqlite3VdbeMemRelease(m);
            return UnitTest.SQLITE_CORRUPT_BKPT();
        }

        /*
    ** Compare the key of the index entry that cursor pC is pointing to against
    ** the key string in pUnpacked.  Write into *pRes a number
    ** that is negative, zero, or positive if pC is less than, equal to,
    ** or greater than pUnpacked.  Return StatusCode.SQLITE_OK on success.
    **
    ** pUnpacked is either created without a rowid or is truncated so that it
    ** omits the rowid at the end.  The rowid at the end of the index entry
    ** is ignored as well.  Hence, this routine only compares the prefixes 
    ** of the keys prior to the final rowid, not the entire key.
    */

        public static int VdbeIdxKeyCompare(
            VdbeCursor pC, /* The cursor to compare against */
            UnpackedRecord pUnpacked, /* Unpacked version of key to compare against */
            ref int res /* Write the comparison result here */
            )
        {
            long nCellKey = 0;
            int rc;
            BtCursor pCur = pC.pCursor;
            Mem m = null;

            Debug.Assert(BTreeHelper.BtreeCursorIsValid(pCur));
            rc = BTreeHelper.BtreeKeySize(pCur, ref nCellKey);
            Debug.Assert(rc == StatusCode.SQLITE_OK); /* pCur is always valid so KeySize cannot fail */
            /* nCellKey will always be between 0 and 0xffffffff because of the say
      ** that btreeParseCellPtr() and Utility.Sqlite3GetVarint32() are implemented */
            if (nCellKey <= 0 || nCellKey > 0x7fffffff)
            {
                res = 0;
                return UnitTest.SQLITE_CORRUPT_BKPT();
            }

            m = Malloc.sqlite3Malloc(m);
            // memset(&m, 0, sizeof(m));
            rc = sqlite3VdbeMemFromBtree(pC.pCursor, 0, (int)nCellKey, true, m);
            if (rc != 0)
            {
                return rc;
            }
            Debug.Assert((pUnpacked.flags & UnpackedRecordFlag.UNPACKED_IGNORE_ROWID) != 0);
            res = VdbeAux.VdbeRecordCompare(m.n, m.zBLOB, pUnpacked);
            sqlite3VdbeMemRelease(m);
            return StatusCode.SQLITE_OK;
        }

        /*
    ** This routine sets the value to be returned by subsequent calls to
    ** sqlite3_changes() on the database handle 'db'.
    */

        public static void VdbeSetChanges(sqlite3 db, int nChange)
        {
            Debug.Assert(MutexHelper.MutexHeld(db.mutex));
            db.nChange = nChange;
            db.nTotalChange += nChange;
        }

        /*
    ** Set a flag in the vdbe to update the change counter when it is finalised
    ** or reset.
    */

        public static void VdbeCountChanges(Vdbe v)
        {
            v.changeCntOn = true;
        }

        /*
    ** Mark every prepared statement associated with a database connection
    ** as expired.
    **
    ** An expired statement means that recompilation of the statement is
    ** recommend.  Statements expire when things happen that make their
    ** programs obsolete.  Removing user-defined functions or collating
    ** sequences, or changing an authorization function are the types of
    ** things that make prepared statements obsolete.
    */

        public static void sqlite3ExpirePreparedStatements(sqlite3 db)
        {
            Vdbe p;
            for (p = db.pVdbe; p != null; p = p.pNext)
            {
                p.expired = true;
            }
        }

        /*
    ** Return the database associated with the Vdbe.
    */

        public static sqlite3 VdbeDb(Vdbe v)
        {
            return v.db;
        }

        /*
    ** Return a pointer to an sqlite3_value structure containing the value bound
    ** parameter iVar of VM v. Except, if the value is an SQL NULL, return 
    ** 0 instead. Unless it is NULL, apply affinity aff (one of the SQLITE_AFF_*
    ** constants) to the value before returning it.
    **
    ** The returned value must be freed by the caller using sqlite3ValueFree().
    */

        public static sqlite3_value VdbeGetValue(Vdbe v, int iVar, byte aff)
        {
            Debug.Assert(iVar > 0);
            if (v != null)
            {
                Mem pMem = v.aVar[iVar - 1];
                if (0 == (pMem.flags & MEMFlag.MEM_Null))
                {
                    sqlite3_value pRet = sqlite3ValueNew(v.db);
                    if (pRet != null)
                    {
                        sqlite3VdbeMemCopy(pRet, pMem);
                        sqlite3ValueApplyAffinity(pRet, (char)aff, Const.SQLITE_UTF8);
                        sqlite3VdbeMemStoreType(pRet);
                    }
                    return pRet;
                }
            }
            return null;
        }

        /*
    ** Configure SQL variable iVar so that binding a new value to it signals
    ** to sqlite3_reoptimize() that re-preparing the statement may result
    ** in a better query plan.
    */

        public static void VdbeSetVarmask(Vdbe v, int iVar)
        {
            Debug.Assert(iVar > 0);
            if (iVar > 32)
            {
                v.expmask = 0xffffffff;
            }
            else
            {
                v.expmask |= ((uint)1 << (iVar - 1));
            }
        }
    }
}